Gentoo Archives: gentoo-commits

From: Zac Medico <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:master commit in: pym/_emerge/resolver/, pym/portage/package/ebuild/, pym/portage/dbapi/, ...
Date: Thu, 17 Nov 2011 23:10:32
Message-Id: d3f704a425a50b5cfa997a25866929b30f1b7d0f.zmedico@gentoo
1 commit: d3f704a425a50b5cfa997a25866929b30f1b7d0f
2 Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
3 AuthorDate: Thu Nov 17 23:10:13 2011 +0000
4 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
5 CommitDate: Thu Nov 17 23:10:13 2011 +0000
6 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=d3f704a4
7
8 Skip the "resume after portage update" routine.
9
10 Instead, finish the whole job using a copy of the currently running
11 instance. This allows us to avoid the complexities of emerge --resume,
12 such as the differences in option handling between different portage
13 versions, as reported in bug #390819.
14
15 ---
16 pym/_emerge/Scheduler.py | 143 +++++++++-----------------------
17 pym/_emerge/depgraph.py | 2 -
18 pym/_emerge/resolver/output.py | 21 -----
19 pym/_emerge/resolver/output_helpers.py | 1 -
20 pym/portage/dbapi/_MergeProcess.py | 58 -------------
21 pym/portage/package/ebuild/config.py | 17 +++--
22 pym/portage/package/ebuild/doebuild.py | 44 ++++++++++-
23 7 files changed, 92 insertions(+), 194 deletions(-)
24
25 diff --git a/pym/_emerge/Scheduler.py b/pym/_emerge/Scheduler.py
26 index 3b53a37..393eeb6 100644
27 --- a/pym/_emerge/Scheduler.py
28 +++ b/pym/_emerge/Scheduler.py
29 @@ -29,7 +29,8 @@ from portage._sets.base import InternalPackageSet
30 from portage.util import ensure_dirs, writemsg, writemsg_level
31 from portage.package.ebuild.digestcheck import digestcheck
32 from portage.package.ebuild.digestgen import digestgen
33 -from portage.package.ebuild.doebuild import _check_temp_dir
34 +from portage.package.ebuild.doebuild import (_check_temp_dir,
35 + _prepare_self_update)
36 from portage.package.ebuild.prepare_build_dirs import prepare_build_dirs
37
38 import _emerge
39 @@ -75,12 +76,9 @@ class Scheduler(PollScheduler):
40 frozenset(["--pretend",
41 "--fetchonly", "--fetch-all-uri"])
42
43 - _opts_no_restart = frozenset(["--buildpkgonly",
44 + _opts_no_self_reinstall = frozenset(["--buildpkgonly",
45 "--fetchonly", "--fetch-all-uri", "--pretend"])
46
47 - _bad_resume_opts = set(["--ask", "--changelog",
48 - "--resume", "--skipfirst"])
49 -
50 class _iface_class(SlotObject):
51 __slots__ = ("fetch",
52 "output", "register", "schedule",
53 @@ -289,6 +287,38 @@ class Scheduler(PollScheduler):
54 self._running_portage = self._pkg(cpv, "installed",
55 self._running_root, installed=True)
56
57 + def _handle_self_update(self):
58 + """
59 + If portage is updating itself, create temporary
60 + copies of PORTAGE_BIN_PATH and PORTAGE_PYM_PATH in order
61 + to avoid relying on the new versions which may be
62 + incompatible. Register an atexit hook to clean up the
63 + temporary directories. Pre-load elog modules here since
64 + we won't be able to later if they get unmerged (happens
65 + when namespace changes).
66 + """
67 +
68 + if self._opts_no_self_reinstall.intersection(self.myopts):
69 + return
70 +
71 + for x in self._mergelist:
72 + if not isinstance(x, Package):
73 + continue
74 + if x.operation != "merge":
75 + continue
76 + if x.root != self._running_root.root:
77 + continue
78 + if not portage.dep.match_from_list(
79 + portage.const.PORTAGE_PACKAGE_ATOM, [x]):
80 + continue
81 + if self._running_portage is None or \
82 + self._running_portage.cpv != x.cpv or \
83 + '9999' in x.cpv or \
84 + 'git' in x.inherited or \
85 + 'git-2' in x.inherited:
86 + _prepare_self_update(self.settings)
87 + break
88 +
89 def _terminate_tasks(self):
90 self._status_display.quiet = True
91 while self._running_tasks:
92 @@ -785,100 +815,6 @@ class Scheduler(PollScheduler):
93
94 return prefetcher
95
96 - def _is_restart_scheduled(self):
97 - """
98 - Check if the merge list contains a replacement
99 - for the current running instance, that will result
100 - in restart after merge.
101 - @rtype: bool
102 - @returns: True if a restart is scheduled, False otherwise.
103 - """
104 - if self._opts_no_restart.intersection(self.myopts):
105 - return False
106 -
107 - mergelist = self._mergelist
108 -
109 - for i, pkg in enumerate(mergelist):
110 - if self._is_restart_necessary(pkg) and \
111 - i != len(mergelist) - 1:
112 - return True
113 -
114 - return False
115 -
116 - def _is_restart_necessary(self, pkg):
117 - """
118 - @return: True if merging the given package
119 - requires restart, False otherwise.
120 - """
121 -
122 - # Figure out if we need a restart.
123 - if pkg.root == self._running_root.root and \
124 - portage.match_from_list(
125 - portage.const.PORTAGE_PACKAGE_ATOM, [pkg]):
126 - if self._running_portage is None:
127 - return True
128 - elif pkg.cpv != self._running_portage.cpv or \
129 - '9999' in pkg.cpv or \
130 - 'git' in pkg.inherited or \
131 - 'git-2' in pkg.inherited:
132 - return True
133 - return False
134 -
135 - def _restart_if_necessary(self, pkg):
136 - """
137 - Use execv() to restart emerge. This happens
138 - if portage upgrades itself and there are
139 - remaining packages in the list.
140 - """
141 -
142 - if self._opts_no_restart.intersection(self.myopts):
143 - return
144 -
145 - if not self._is_restart_necessary(pkg):
146 - return
147 -
148 - if pkg == self._mergelist[-1]:
149 - return
150 -
151 - self._main_loop_cleanup()
152 -
153 - logger = self._logger
154 - pkg_count = self._pkg_count
155 - mtimedb = self._mtimedb
156 - bad_resume_opts = self._bad_resume_opts
157 -
158 - logger.log(" ::: completed emerge (%s of %s) %s to %s" % \
159 - (pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg.root))
160 -
161 - logger.log(" *** RESTARTING " + \
162 - "emerge via exec() after change of " + \
163 - "portage version.")
164 -
165 - mtimedb["resume"]["mergelist"].remove(list(pkg))
166 - mtimedb.commit()
167 - portage.run_exitfuncs()
168 - # Don't trust sys.argv[0] here because eselect-python may modify it.
169 - emerge_binary = os.path.join(portage.const.PORTAGE_BIN_PATH, 'emerge')
170 - mynewargv = [emerge_binary, "--resume"]
171 - resume_opts = self.myopts.copy()
172 - # For automatic resume, we need to prevent
173 - # any of bad_resume_opts from leaking in
174 - # via EMERGE_DEFAULT_OPTS.
175 - resume_opts["--ignore-default-opts"] = True
176 - for myopt, myarg in resume_opts.items():
177 - if myopt not in bad_resume_opts:
178 - if myarg is True:
179 - mynewargv.append(myopt)
180 - elif isinstance(myarg, list):
181 - # arguments like --exclude that use 'append' action
182 - for x in myarg:
183 - mynewargv.append("%s=%s" % (myopt, x))
184 - else:
185 - mynewargv.append("%s=%s" % (myopt, myarg))
186 - # priority only needs to be adjusted on the first run
187 - os.environ["PORTAGE_NICENESS"] = "0"
188 - os.execv(mynewargv[0], mynewargv)
189 -
190 def _run_pkg_pretend(self):
191 """
192 Since pkg_pretend output may be important, this method sends all
193 @@ -1034,6 +970,8 @@ class Scheduler(PollScheduler):
194 except self._unknown_internal_error:
195 return 1
196
197 + self._handle_self_update()
198 +
199 for root in self.trees:
200 root_config = self.trees[root]["root_config"]
201
202 @@ -1379,8 +1317,6 @@ class Scheduler(PollScheduler):
203 if pkg.installed:
204 return
205
206 - self._restart_if_necessary(pkg)
207 -
208 # Call mtimedb.commit() after each merge so that
209 # --resume still works after being interrupted
210 # by reboot, sigkill or similar.
211 @@ -1585,10 +1521,7 @@ class Scheduler(PollScheduler):
212
213 def _main_loop(self):
214
215 - # Only allow 1 job max if a restart is scheduled
216 - # due to portage update.
217 - if self._is_restart_scheduled() or \
218 - self._opts_no_background.intersection(self.myopts):
219 + if self._opts_no_background.intersection(self.myopts):
220 self._set_max_jobs(1)
221
222 while self._schedule():
223
224 diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
225 index fda335f..65fe1fd 100644
226 --- a/pym/_emerge/depgraph.py
227 +++ b/pym/_emerge/depgraph.py
228 @@ -100,8 +100,6 @@ class _frozen_depgraph_config(object):
229 self.edebug = 1
230 self.spinner = spinner
231 self._running_root = trees[trees._running_eroot]["root_config"]
232 - self._opts_no_restart = frozenset(["--buildpkgonly",
233 - "--fetchonly", "--fetch-all-uri", "--pretend"])
234 self.pkgsettings = {}
235 self.trees = {}
236 self._trees_orig = trees
237
238 diff --git a/pym/_emerge/resolver/output.py b/pym/_emerge/resolver/output.py
239 index eed3019..6f1c76c 100644
240 --- a/pym/_emerge/resolver/output.py
241 +++ b/pym/_emerge/resolver/output.py
242 @@ -865,27 +865,6 @@ class Display(object):
243 continue
244 self.print_msg.append((myprint, self.verboseadd, self.repoadd))
245
246 - if not self.conf.tree_display \
247 - and not self.conf.no_restart \
248 - and pkg.root == self.conf.running_root.root \
249 - and match_from_list(PORTAGE_PACKAGE_ATOM, [pkg]) \
250 - and not self.conf.quiet:
251 -
252 - if not self.vardb.cpv_exists(pkg.cpv) or \
253 - '9999' in pkg.cpv or \
254 - 'git' in pkg.inherited or \
255 - 'git-2' in pkg.inherited:
256 - if mylist_index < len(mylist) - 1:
257 - self.print_msg.append(
258 - colorize(
259 - "WARN", "*** Portage will stop merging "
260 - "at this point and reload itself,"
261 - )
262 - )
263 - self.print_msg.append(
264 - colorize("WARN", " then resume the merge.")
265 - )
266 -
267 show_repos = repoadd_set and repoadd_set != set(["0"])
268
269 # now finally print out the messages
270
271 diff --git a/pym/_emerge/resolver/output_helpers.py b/pym/_emerge/resolver/output_helpers.py
272 index b3cdbc4..dd26534 100644
273 --- a/pym/_emerge/resolver/output_helpers.py
274 +++ b/pym/_emerge/resolver/output_helpers.py
275 @@ -198,7 +198,6 @@ class _DisplayConfig(object):
276 self.print_use_string = self.verbosity != 1 or "--verbose" in frozen_config.myopts
277 self.changelog = "--changelog" in frozen_config.myopts
278 self.edebug = frozen_config.edebug
279 - self.no_restart = frozen_config._opts_no_restart.intersection(frozen_config.myopts)
280 self.unordered_display = "--unordered-display" in frozen_config.myopts
281
282 mywidth = 130
283
284 diff --git a/pym/portage/dbapi/_MergeProcess.py b/pym/portage/dbapi/_MergeProcess.py
285 index 34ed031..c9b6288 100644
286 --- a/pym/portage/dbapi/_MergeProcess.py
287 +++ b/pym/portage/dbapi/_MergeProcess.py
288 @@ -2,20 +2,14 @@
289 # Distributed under the terms of the GNU General Public License v2
290
291 import io
292 -import shutil
293 import signal
294 -import tempfile
295 import traceback
296
297 import errno
298 import fcntl
299 import portage
300 from portage import os, _unicode_decode
301 -from portage.const import PORTAGE_PACKAGE_ATOM
302 -from portage.dep import match_from_list
303 import portage.elog.messages
304 -from portage.elog import _preload_elog_modules
305 -from portage.util import ensure_dirs
306 from _emerge.PollConstants import PollConstants
307 from _emerge.SpawnProcess import SpawnProcess
308
309 @@ -46,8 +40,6 @@ class MergeProcess(SpawnProcess):
310 settings.reset()
311 settings.setcpv(cpv, mydb=self.mydbapi)
312
313 - if not self.unmerge:
314 - self._handle_self_reinstall()
315 super(MergeProcess, self)._start()
316
317 def _lock_vdb(self):
318 @@ -69,56 +61,6 @@ class MergeProcess(SpawnProcess):
319 self.vartree.dbapi.unlock()
320 self._locked_vdb = False
321
322 - def _handle_self_reinstall(self):
323 - """
324 - If portage is reinstalling itself, create temporary
325 - copies of PORTAGE_BIN_PATH and PORTAGE_PYM_PATH in order
326 - to avoid relying on the new versions which may be
327 - incompatible. Register an atexit hook to clean up the
328 - temporary directories. Pre-load elog modules here since
329 - we won't be able to later if they get unmerged (happens
330 - when namespace changes).
331 - """
332 -
333 - settings = self.settings
334 - cpv = settings.mycpv
335 - reinstall_self = False
336 - if self.settings["ROOT"] == "/" and \
337 - match_from_list(PORTAGE_PACKAGE_ATOM, [cpv]):
338 - inherited = frozenset(self.settings.get('INHERITED', '').split())
339 - if not self.vartree.dbapi.cpv_exists(cpv) or \
340 - '9999' in cpv or \
341 - 'git' in inherited or \
342 - 'git-2' in inherited:
343 - reinstall_self = True
344 -
345 - if reinstall_self:
346 - # Load lazily referenced portage submodules into memory,
347 - # so imports won't fail during portage upgrade/downgrade.
348 - _preload_elog_modules(self.settings)
349 - portage.proxy.lazyimport._preload_portage_submodules()
350 -
351 - # Make the temp directory inside $PORTAGE_TMPDIR/portage, since
352 - # it's common for /tmp and /var/tmp to be mounted with the
353 - # "noexec" option (see bug #346899).
354 - build_prefix = os.path.join(settings["PORTAGE_TMPDIR"], "portage")
355 - ensure_dirs(build_prefix)
356 - base_path_tmp = tempfile.mkdtemp(
357 - "", "._portage_reinstall_.", build_prefix)
358 - portage.process.atexit_register(shutil.rmtree, base_path_tmp)
359 - dir_perms = 0o755
360 - for subdir in "bin", "pym":
361 - var_name = "PORTAGE_%s_PATH" % subdir.upper()
362 - var_orig = settings[var_name]
363 - var_new = os.path.join(base_path_tmp, subdir)
364 - settings[var_name] = var_new
365 - settings.backup_changes(var_name)
366 - shutil.copytree(var_orig, var_new, symlinks=True)
367 - os.chmod(var_new, dir_perms)
368 - portage._bin_path = settings['PORTAGE_BIN_PATH']
369 - portage._pym_path = settings['PORTAGE_PYM_PATH']
370 - os.chmod(base_path_tmp, dir_perms)
371 -
372 def _elog_output_handler(self, fd, event):
373 output = None
374 if event & PollConstants.POLLIN:
375
376 diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py
377 index 765a4f7..6d5de92 100644
378 --- a/pym/portage/package/ebuild/config.py
379 +++ b/pym/portage/package/ebuild/config.py
380 @@ -22,7 +22,7 @@ from portage import bsd_chflags, \
381 load_mod, os, selinux, _unicode_decode
382 from portage.const import CACHE_PATH, \
383 DEPCACHE_PATH, INCREMENTALS, MAKE_CONF_FILE, \
384 - MODULES_FILE_PATH, PORTAGE_BIN_PATH, PORTAGE_PYM_PATH, \
385 + MODULES_FILE_PATH, \
386 PRIVATE_PATH, PROFILE_PATH, USER_CONFIG_PATH, \
387 USER_VIRTUALS_FILE
388 from portage.const import _SANDBOX_COMPAT_LEVEL
389 @@ -722,11 +722,6 @@ class config(object):
390 self["USERLAND"] = "GNU"
391 self.backup_changes("USERLAND")
392
393 - self["PORTAGE_BIN_PATH"] = PORTAGE_BIN_PATH
394 - self.backup_changes("PORTAGE_BIN_PATH")
395 - self["PORTAGE_PYM_PATH"] = PORTAGE_PYM_PATH
396 - self.backup_changes("PORTAGE_PYM_PATH")
397 -
398 for var in ("PORTAGE_INST_UID", "PORTAGE_INST_GID"):
399 try:
400 self[var] = str(int(self.get(var, "0")))
401 @@ -2088,6 +2083,14 @@ class config(object):
402 del x[mykey]
403
404 def __getitem__(self,mykey):
405 +
406 + # These ones point to temporary values when
407 + # portage plans to update itself.
408 + if mykey == "PORTAGE_BIN_PATH":
409 + return portage._bin_path
410 + elif mykey == "PORTAGE_PYM_PATH":
411 + return portage._pym_path
412 +
413 for d in self.lookuplist:
414 if mykey in d:
415 return d[mykey]
416 @@ -2133,6 +2136,8 @@ class config(object):
417
418 def __iter__(self):
419 keys = set()
420 + keys.add("PORTAGE_BIN_PATH")
421 + keys.add("PORTAGE_PYM_PATH")
422 for d in self.lookuplist:
423 keys.update(d)
424 return iter(keys)
425
426 diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py
427 index bdfcbcc..49d3e89 100644
428 --- a/pym/portage/package/ebuild/doebuild.py
429 +++ b/pym/portage/package/ebuild/doebuild.py
430 @@ -44,7 +44,7 @@ from portage.dep import Atom, check_required_use, \
431 from portage.eapi import eapi_exports_KV, eapi_exports_merge_type, \
432 eapi_exports_replace_vars, eapi_has_required_use, \
433 eapi_has_src_prepare_and_src_configure, eapi_has_pkg_pretend
434 -from portage.elog import elog_process
435 +from portage.elog import elog_process, _preload_elog_modules
436 from portage.elog.messages import eerror, eqawarn
437 from portage.exception import DigestException, FileNotFound, \
438 IncorrectParameter, InvalidDependString, PermissionDenied, \
439 @@ -1027,6 +1027,7 @@ def doebuild(myebuild, mydo, _unused=None, settings=None, debug=0, listonly=0,
440 # qmerge is a special phase that implies noclean.
441 if "noclean" not in mysettings.features:
442 mysettings.features.add("noclean")
443 + _handle_self_update(mysettings, vartree.dbapi)
444 #qmerge is specifically not supposed to do a runtime dep check
445 retval = merge(
446 mysettings["CATEGORY"], mysettings["PF"], mysettings["D"],
447 @@ -1043,6 +1044,7 @@ def doebuild(myebuild, mydo, _unused=None, settings=None, debug=0, listonly=0,
448 # so that it's only called once.
449 elog_process(mysettings.mycpv, mysettings)
450 if retval == os.EX_OK:
451 + _handle_self_update(mysettings, vartree.dbapi)
452 retval = merge(mysettings["CATEGORY"], mysettings["PF"],
453 mysettings["D"], os.path.join(mysettings["PORTAGE_BUILDDIR"],
454 "build-info"), myroot, mysettings,
455 @@ -2013,3 +2015,43 @@ def _merge_unicode_error(errors):
456 lines.append("")
457
458 return lines
459 +
460 +def _prepare_self_update(settings):
461 + # Load lazily referenced portage submodules into memory,
462 + # so imports won't fail during portage upgrade/downgrade.
463 + _preload_elog_modules(settings)
464 + portage.proxy.lazyimport._preload_portage_submodules()
465 +
466 + # Make the temp directory inside $PORTAGE_TMPDIR/portage, since
467 + # it's common for /tmp and /var/tmp to be mounted with the
468 + # "noexec" option (see bug #346899).
469 + build_prefix = os.path.join(settings["PORTAGE_TMPDIR"], "portage")
470 + portage.util.ensure_dirs(build_prefix)
471 + base_path_tmp = tempfile.mkdtemp(
472 + "", "._portage_reinstall_.", build_prefix)
473 + portage.process.atexit_register(shutil.rmtree, base_path_tmp)
474 +
475 + orig_bin_path = portage._bin_path
476 + portage._bin_path = os.path.join(base_path_tmp, "bin")
477 + shutil.copytree(orig_bin_path, portage._bin_path, symlinks=True)
478 +
479 + orig_pym_path = portage._pym_path
480 + portage._pym_path = os.path.join(base_path_tmp, "pym")
481 + shutil.copytree(orig_pym_path, portage._pym_path, symlinks=True)
482 +
483 + for dir_path in (base_path_tmp, portage._bin_path, portage._pym_path):
484 + os.chmod(dir_path, 0o755)
485 +
486 +def _handle_self_update(settings, vardb):
487 + cpv = settings.mycpv
488 + if settings["ROOT"] == "/" and \
489 + portage.dep.match_from_list(
490 + portage.const.PORTAGE_PACKAGE_ATOM, [cpv]):
491 + inherited = frozenset(settings.get('INHERITED', '').split())
492 + if not vardb.cpv_exists(cpv) or \
493 + '9999' in cpv or \
494 + 'git' in inherited or \
495 + 'git-2' in inherited:
496 + _prepare_self_update(settings)
497 + return True
498 + return False