blob: e3fe2109ca6d8c6c0702093fd917f54598e864e6 [file] [log] [blame]
Mike Frysinger0a647fc2012-08-06 14:36:05 -04001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
David Jamesfcb70ef2011-02-02 16:02:30 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Program to run emerge in parallel, for significant speedup.
6
7Usage:
David James386ccd12011-05-04 20:17:42 -07008 ./parallel_emerge [--board=BOARD] [--workon=PKGS]
David Jamesfcb70ef2011-02-02 16:02:30 -08009 [--force-remote-binary=PKGS] [emerge args] package
10
David James78b6cd92012-04-02 21:36:12 -070011This script runs multiple emerge processes in parallel, using appropriate
12Portage APIs. It is faster than standard emerge because it has a
13multiprocess model instead of an asynchronous model.
David Jamesfcb70ef2011-02-02 16:02:30 -080014"""
15
Mike Frysinger383367e2014-09-16 15:06:17 -040016from __future__ import print_function
17
David Jamesfcb70ef2011-02-02 16:02:30 -080018import codecs
19import copy
20import errno
Brian Harring8294d652012-05-23 02:20:52 -070021import gc
David James8c7e5e32011-06-28 11:26:03 -070022import heapq
David Jamesfcb70ef2011-02-02 16:02:30 -080023import multiprocessing
24import os
Mike Frysinger1ae28092013-10-17 17:17:22 -040025try:
26 import Queue
27except ImportError:
28 # Python-3 renamed to "queue". We still use Queue to avoid collisions
29 # with naming variables as "queue". Maybe we'll transition at some point.
30 # pylint: disable=F0401
31 import queue as Queue
David Jamesfcb70ef2011-02-02 16:02:30 -080032import signal
Bertrand SIMONNET19d789e2014-12-09 13:36:31 -080033import subprocess
David Jamesfcb70ef2011-02-02 16:02:30 -080034import sys
35import tempfile
Brian Harring8294d652012-05-23 02:20:52 -070036import threading
David Jamesfcb70ef2011-02-02 16:02:30 -080037import time
38import traceback
David Jamesfcb70ef2011-02-02 16:02:30 -080039
Thiago Goncalesf4acc422013-07-17 10:26:35 -070040from chromite.lib import cros_build_lib
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040041from chromite.lib import process_util
Mike Frysingerd74fe4a2014-04-24 11:43:38 -040042from chromite.lib import proctitle
Thiago Goncalesf4acc422013-07-17 10:26:35 -070043
David Jamesfcb70ef2011-02-02 16:02:30 -080044# If PORTAGE_USERNAME isn't specified, scrape it from the $HOME variable. On
45# Chromium OS, the default "portage" user doesn't have the necessary
46# permissions. It'd be easier if we could default to $USERNAME, but $USERNAME
47# is "root" here because we get called through sudo.
48#
49# We need to set this before importing any portage modules, because portage
50# looks up "PORTAGE_USERNAME" at import time.
51#
52# NOTE: .bashrc sets PORTAGE_USERNAME = $USERNAME, so most people won't
53# encounter this case unless they have an old chroot or blow away the
54# environment by running sudo without the -E specifier.
55if "PORTAGE_USERNAME" not in os.environ:
56 homedir = os.environ.get("HOME")
57 if homedir:
58 os.environ["PORTAGE_USERNAME"] = os.path.basename(homedir)
59
Bertrand SIMONNET19d789e2014-12-09 13:36:31 -080060# Wrap Popen with a lock to ensure no two Popen are executed simultaneously in
61# the same process.
62# Two Popen call at the same time might be the cause for crbug.com/433482.
63_popen_lock = threading.Lock()
64_old_popen = subprocess.Popen
65
66def _LockedPopen(*args, **kwargs):
67 with _popen_lock:
68 return _old_popen(*args, **kwargs)
69
70subprocess.Popen = _LockedPopen
71
David Jamesfcb70ef2011-02-02 16:02:30 -080072# Portage doesn't expose dependency trees in its public API, so we have to
73# make use of some private APIs here. These modules are found under
74# /usr/lib/portage/pym/.
75#
76# TODO(davidjames): Update Portage to expose public APIs for these features.
Don Garrett25f309a2014-03-19 14:02:12 -070077# pylint: disable=F0401
David Jamesfcb70ef2011-02-02 16:02:30 -080078from _emerge.actions import adjust_configs
79from _emerge.actions import load_emerge_config
80from _emerge.create_depgraph_params import create_depgraph_params
David James386ccd12011-05-04 20:17:42 -070081from _emerge.depgraph import backtrack_depgraph
David Jamesfcb70ef2011-02-02 16:02:30 -080082from _emerge.main import emerge_main
83from _emerge.main import parse_opts
84from _emerge.Package import Package
Bertrand SIMONNETa15b5072014-10-23 15:27:52 -070085from _emerge.post_emerge import clean_logs
David Jamesfcb70ef2011-02-02 16:02:30 -080086from _emerge.Scheduler import Scheduler
David Jamesfcb70ef2011-02-02 16:02:30 -080087from _emerge.stdout_spinner import stdout_spinner
David James386ccd12011-05-04 20:17:42 -070088from portage._global_updates import _global_updates
David Jamesfcb70ef2011-02-02 16:02:30 -080089import portage
90import portage.debug
Don Garrettf8bf7842014-03-20 17:03:42 -070091# pylint: enable=F0401
Mike Frysinger91d7da92013-02-19 15:53:46 -050092
David Jamesfcb70ef2011-02-02 16:02:30 -080093
David Jamesfcb70ef2011-02-02 16:02:30 -080094def Usage():
95 """Print usage."""
Mike Frysinger383367e2014-09-16 15:06:17 -040096 print("Usage:")
97 print(" ./parallel_emerge [--board=BOARD] [--workon=PKGS]")
98 print(" [--rebuild] [emerge args] package")
99 print()
100 print("Packages specified as workon packages are always built from source.")
101 print()
102 print("The --workon argument is mainly useful when you want to build and")
103 print("install packages that you are working on unconditionally, but do not")
104 print("to have to rev the package to indicate you want to build it from")
105 print("source. The build_packages script will automatically supply the")
106 print("workon argument to emerge, ensuring that packages selected using")
107 print("cros-workon are rebuilt.")
108 print()
109 print("The --rebuild option rebuilds packages whenever their dependencies")
110 print("are changed. This ensures that your build is correct.")
David Jamesfcb70ef2011-02-02 16:02:30 -0800111
112
David Jamesfcb70ef2011-02-02 16:02:30 -0800113# Global start time
114GLOBAL_START = time.time()
115
David James7358d032011-05-19 10:40:03 -0700116# Whether process has been killed by a signal.
117KILLED = multiprocessing.Event()
118
David Jamesfcb70ef2011-02-02 16:02:30 -0800119
120class EmergeData(object):
121 """This simple struct holds various emerge variables.
122
123 This struct helps us easily pass emerge variables around as a unit.
124 These variables are used for calculating dependencies and installing
125 packages.
126 """
127
David Jamesbf1e3442011-05-28 07:44:20 -0700128 __slots__ = ["action", "cmdline_packages", "depgraph", "favorites",
129 "mtimedb", "opts", "root_config", "scheduler_graph",
130 "settings", "spinner", "trees"]
David Jamesfcb70ef2011-02-02 16:02:30 -0800131
132 def __init__(self):
133 # The action the user requested. If the user is installing packages, this
134 # is None. If the user is doing anything other than installing packages,
135 # this will contain the action name, which will map exactly to the
136 # long-form name of the associated emerge option.
137 #
138 # Example: If you call parallel_emerge --unmerge package, the action name
139 # will be "unmerge"
140 self.action = None
141
142 # The list of packages the user passed on the command-line.
143 self.cmdline_packages = None
144
145 # The emerge dependency graph. It'll contain all the packages involved in
146 # this merge, along with their versions.
147 self.depgraph = None
148
David Jamesbf1e3442011-05-28 07:44:20 -0700149 # The list of candidates to add to the world file.
150 self.favorites = None
151
David Jamesfcb70ef2011-02-02 16:02:30 -0800152 # A dict of the options passed to emerge. This dict has been cleaned up
153 # a bit by parse_opts, so that it's a bit easier for the emerge code to
154 # look at the options.
155 #
156 # Emerge takes a few shortcuts in its cleanup process to make parsing of
157 # the options dict easier. For example, if you pass in "--usepkg=n", the
158 # "--usepkg" flag is just left out of the dictionary altogether. Because
159 # --usepkg=n is the default, this makes parsing easier, because emerge
160 # can just assume that if "--usepkg" is in the dictionary, it's enabled.
161 #
162 # These cleanup processes aren't applied to all options. For example, the
163 # --with-bdeps flag is passed in as-is. For a full list of the cleanups
164 # applied by emerge, see the parse_opts function in the _emerge.main
165 # package.
166 self.opts = None
167
168 # A dictionary used by portage to maintain global state. This state is
169 # loaded from disk when portage starts up, and saved to disk whenever we
170 # call mtimedb.commit().
171 #
172 # This database contains information about global updates (i.e., what
173 # version of portage we have) and what we're currently doing. Portage
174 # saves what it is currently doing in this database so that it can be
175 # resumed when you call it with the --resume option.
176 #
177 # parallel_emerge does not save what it is currently doing in the mtimedb,
178 # so we do not support the --resume option.
179 self.mtimedb = None
180
181 # The portage configuration for our current root. This contains the portage
182 # settings (see below) and the three portage trees for our current root.
183 # (The three portage trees are explained below, in the documentation for
184 # the "trees" member.)
185 self.root_config = None
186
187 # The scheduler graph is used by emerge to calculate what packages to
188 # install. We don't actually install any deps, so this isn't really used,
189 # but we pass it in to the Scheduler object anyway.
190 self.scheduler_graph = None
191
192 # Portage settings for our current session. Most of these settings are set
193 # in make.conf inside our current install root.
194 self.settings = None
195
196 # The spinner, which spews stuff to stdout to indicate that portage is
197 # doing something. We maintain our own spinner, so we set the portage
198 # spinner to "silent" mode.
199 self.spinner = None
200
201 # The portage trees. There are separate portage trees for each root. To get
202 # the portage tree for the current root, you can look in self.trees[root],
203 # where root = self.settings["ROOT"].
204 #
205 # In each root, there are three trees: vartree, porttree, and bintree.
206 # - vartree: A database of the currently-installed packages.
207 # - porttree: A database of ebuilds, that can be used to build packages.
208 # - bintree: A database of binary packages.
209 self.trees = None
210
211
212class DepGraphGenerator(object):
213 """Grab dependency information about packages from portage.
214
215 Typical usage:
216 deps = DepGraphGenerator()
217 deps.Initialize(sys.argv[1:])
218 deps_tree, deps_info = deps.GenDependencyTree()
219 deps_graph = deps.GenDependencyGraph(deps_tree, deps_info)
220 deps.PrintTree(deps_tree)
221 PrintDepsMap(deps_graph)
222 """
223
Bertrand SIMONNET0625e1a2015-04-07 11:41:16 -0700224 __slots__ = ["board", "emerge", "package_db", "show_output", "sysroot",
Bertrand SIMONNET411945d2015-05-20 17:23:28 -0700225 "unpack_only", "max_retries"]
David Jamesfcb70ef2011-02-02 16:02:30 -0800226
227 def __init__(self):
228 self.board = None
229 self.emerge = EmergeData()
David Jamesfcb70ef2011-02-02 16:02:30 -0800230 self.package_db = {}
David Jamesfcb70ef2011-02-02 16:02:30 -0800231 self.show_output = False
Bertrand SIMONNET0625e1a2015-04-07 11:41:16 -0700232 self.sysroot = None
Thiago Goncalesf4acc422013-07-17 10:26:35 -0700233 self.unpack_only = False
Bertrand SIMONNET411945d2015-05-20 17:23:28 -0700234 self.max_retries = 1
David Jamesfcb70ef2011-02-02 16:02:30 -0800235
236 def ParseParallelEmergeArgs(self, argv):
237 """Read the parallel emerge arguments from the command-line.
238
239 We need to be compatible with emerge arg format. We scrape arguments that
240 are specific to parallel_emerge, and pass through the rest directly to
241 emerge.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500242
David Jamesfcb70ef2011-02-02 16:02:30 -0800243 Args:
244 argv: arguments list
Mike Frysinger1a736a82013-12-12 01:50:59 -0500245
David Jamesfcb70ef2011-02-02 16:02:30 -0800246 Returns:
247 Arguments that don't belong to parallel_emerge
248 """
249 emerge_args = []
250 for arg in argv:
251 # Specifically match arguments that are specific to parallel_emerge, and
252 # pass through the rest.
253 if arg.startswith("--board="):
254 self.board = arg.replace("--board=", "")
Bertrand SIMONNET0625e1a2015-04-07 11:41:16 -0700255 elif arg.startswith("--sysroot="):
256 self.sysroot = arg.replace("--sysroot=", "")
David Jamesfcb70ef2011-02-02 16:02:30 -0800257 elif arg.startswith("--workon="):
258 workon_str = arg.replace("--workon=", "")
David James7a1ea4b2011-10-13 15:06:41 -0700259 emerge_args.append("--reinstall-atoms=%s" % workon_str)
260 emerge_args.append("--usepkg-exclude=%s" % workon_str)
David Jamesfcb70ef2011-02-02 16:02:30 -0800261 elif arg.startswith("--force-remote-binary="):
262 force_remote_binary = arg.replace("--force-remote-binary=", "")
David James7a1ea4b2011-10-13 15:06:41 -0700263 emerge_args.append("--useoldpkg-atoms=%s" % force_remote_binary)
Bertrand SIMONNET411945d2015-05-20 17:23:28 -0700264 elif arg.startswith("--retries="):
265 self.max_retries = int(arg.replace("--retries=", ""))
David Jamesfcb70ef2011-02-02 16:02:30 -0800266 elif arg == "--show-output":
267 self.show_output = True
David James386ccd12011-05-04 20:17:42 -0700268 elif arg == "--rebuild":
David James7a1ea4b2011-10-13 15:06:41 -0700269 emerge_args.append("--rebuild-if-unbuilt")
Thiago Goncalesf4acc422013-07-17 10:26:35 -0700270 elif arg == "--unpackonly":
271 emerge_args.append("--fetchonly")
272 self.unpack_only = True
David Jamesfcb70ef2011-02-02 16:02:30 -0800273 else:
274 # Not one of our options, so pass through to emerge.
275 emerge_args.append(arg)
276
David James386ccd12011-05-04 20:17:42 -0700277 # These packages take a really long time to build, so, for expediency, we
278 # are blacklisting them from automatic rebuilds because one of their
279 # dependencies needs to be recompiled.
Mike Frysinger5c2a9052014-04-15 15:52:04 -0400280 for pkg in ("chromeos-base/chromeos-chrome",):
David James7a1ea4b2011-10-13 15:06:41 -0700281 emerge_args.append("--rebuild-exclude=%s" % pkg)
David Jamesfcb70ef2011-02-02 16:02:30 -0800282
283 return emerge_args
284
285 def Initialize(self, args):
286 """Initializer. Parses arguments and sets up portage state."""
287
288 # Parse and strip out args that are just intended for parallel_emerge.
289 emerge_args = self.ParseParallelEmergeArgs(args)
290
Bertrand SIMONNET0625e1a2015-04-07 11:41:16 -0700291 if self.sysroot and self.board:
292 cros_build_lib.Die("--sysroot and --board are incompatible.")
293
David Jamesfcb70ef2011-02-02 16:02:30 -0800294 # Setup various environment variables based on our current board. These
295 # variables are normally setup inside emerge-${BOARD}, but since we don't
296 # call that script, we have to set it up here. These variables serve to
297 # point our tools at /build/BOARD and to setup cross compiles to the
298 # appropriate board as configured in toolchain.conf.
299 if self.board:
Bertrand SIMONNET0625e1a2015-04-07 11:41:16 -0700300 self.sysroot = os.environ.get('SYSROOT',
301 cros_build_lib.GetSysroot(self.board))
302
303 if self.sysroot:
304 os.environ["PORTAGE_CONFIGROOT"] = self.sysroot
305 os.environ["SYSROOT"] = self.sysroot
David Jamesfcb70ef2011-02-02 16:02:30 -0800306
David Jamesfcb70ef2011-02-02 16:02:30 -0800307 # Turn off interactive delays
308 os.environ["EBEEP_IGNORE"] = "1"
309 os.environ["EPAUSE_IGNORE"] = "1"
Mike Frysinger0a647fc2012-08-06 14:36:05 -0400310 os.environ["CLEAN_DELAY"] = "0"
David Jamesfcb70ef2011-02-02 16:02:30 -0800311
312 # Parse the emerge options.
David Jamesea3ca332011-05-26 11:48:29 -0700313 action, opts, cmdline_packages = parse_opts(emerge_args, silent=True)
David Jamesfcb70ef2011-02-02 16:02:30 -0800314
315 # Set environment variables based on options. Portage normally sets these
316 # environment variables in emerge_main, but we can't use that function,
317 # because it also does a bunch of other stuff that we don't want.
318 # TODO(davidjames): Patch portage to move this logic into a function we can
319 # reuse here.
320 if "--debug" in opts:
321 os.environ["PORTAGE_DEBUG"] = "1"
322 if "--config-root" in opts:
323 os.environ["PORTAGE_CONFIGROOT"] = opts["--config-root"]
324 if "--root" in opts:
325 os.environ["ROOT"] = opts["--root"]
326 if "--accept-properties" in opts:
327 os.environ["ACCEPT_PROPERTIES"] = opts["--accept-properties"]
328
David James88d780c2014-02-05 13:03:29 -0800329 # If we're installing packages to the board, we can disable vardb locks.
330 # This is safe because we only run up to one instance of parallel_emerge in
331 # parallel.
332 # TODO(davidjames): Enable this for the host too.
Bertrand SIMONNET0625e1a2015-04-07 11:41:16 -0700333 if self.sysroot:
David Jamesfcb70ef2011-02-02 16:02:30 -0800334 os.environ.setdefault("PORTAGE_LOCKS", "false")
David Jamesfcb70ef2011-02-02 16:02:30 -0800335
336 # Now that we've setup the necessary environment variables, we can load the
337 # emerge config from disk.
338 settings, trees, mtimedb = load_emerge_config()
339
David Jamesea3ca332011-05-26 11:48:29 -0700340 # Add in EMERGE_DEFAULT_OPTS, if specified.
341 tmpcmdline = []
342 if "--ignore-default-opts" not in opts:
343 tmpcmdline.extend(settings["EMERGE_DEFAULT_OPTS"].split())
344 tmpcmdline.extend(emerge_args)
345 action, opts, cmdline_packages = parse_opts(tmpcmdline)
346
347 # If we're installing to the board, we want the --root-deps option so that
348 # portage will install the build dependencies to that location as well.
Bertrand SIMONNET0625e1a2015-04-07 11:41:16 -0700349 if self.sysroot:
David Jamesea3ca332011-05-26 11:48:29 -0700350 opts.setdefault("--root-deps", True)
351
David Jamesfcb70ef2011-02-02 16:02:30 -0800352 # Check whether our portage tree is out of date. Typically, this happens
353 # when you're setting up a new portage tree, such as in setup_board and
354 # make_chroot. In that case, portage applies a bunch of global updates
355 # here. Once the updates are finished, we need to commit any changes
356 # that the global update made to our mtimedb, and reload the config.
357 #
358 # Portage normally handles this logic in emerge_main, but again, we can't
359 # use that function here.
360 if _global_updates(trees, mtimedb["updates"]):
361 mtimedb.commit()
362 settings, trees, mtimedb = load_emerge_config(trees=trees)
363
364 # Setup implied options. Portage normally handles this logic in
365 # emerge_main.
366 if "--buildpkgonly" in opts or "buildpkg" in settings.features:
367 opts.setdefault("--buildpkg", True)
368 if "--getbinpkgonly" in opts:
369 opts.setdefault("--usepkgonly", True)
370 opts.setdefault("--getbinpkg", True)
371 if "getbinpkg" in settings.features:
372 # Per emerge_main, FEATURES=getbinpkg overrides --getbinpkg=n
373 opts["--getbinpkg"] = True
374 if "--getbinpkg" in opts or "--usepkgonly" in opts:
375 opts.setdefault("--usepkg", True)
376 if "--fetch-all-uri" in opts:
377 opts.setdefault("--fetchonly", True)
378 if "--skipfirst" in opts:
379 opts.setdefault("--resume", True)
380 if "--buildpkgonly" in opts:
381 # --buildpkgonly will not merge anything, so it overrides all binary
382 # package options.
383 for opt in ("--getbinpkg", "--getbinpkgonly",
384 "--usepkg", "--usepkgonly"):
385 opts.pop(opt, None)
386 if (settings.get("PORTAGE_DEBUG", "") == "1" and
387 "python-trace" in settings.features):
388 portage.debug.set_trace(True)
389
390 # Complain about unsupported options
David James386ccd12011-05-04 20:17:42 -0700391 for opt in ("--ask", "--ask-enter-invalid", "--resume", "--skipfirst"):
David Jamesfcb70ef2011-02-02 16:02:30 -0800392 if opt in opts:
Mike Frysinger383367e2014-09-16 15:06:17 -0400393 print("%s is not supported by parallel_emerge" % opt)
David Jamesfcb70ef2011-02-02 16:02:30 -0800394 sys.exit(1)
395
396 # Make emerge specific adjustments to the config (e.g. colors!)
397 adjust_configs(opts, trees)
398
399 # Save our configuration so far in the emerge object
400 emerge = self.emerge
401 emerge.action, emerge.opts = action, opts
402 emerge.settings, emerge.trees, emerge.mtimedb = settings, trees, mtimedb
403 emerge.cmdline_packages = cmdline_packages
404 root = settings["ROOT"]
405 emerge.root_config = trees[root]["root_config"]
406
David James386ccd12011-05-04 20:17:42 -0700407 if "--usepkg" in opts:
David Jamesfcb70ef2011-02-02 16:02:30 -0800408 emerge.trees[root]["bintree"].populate("--getbinpkg" in opts)
409
David Jamesfcb70ef2011-02-02 16:02:30 -0800410 def CreateDepgraph(self, emerge, packages):
411 """Create an emerge depgraph object."""
412 # Setup emerge options.
413 emerge_opts = emerge.opts.copy()
414
David James386ccd12011-05-04 20:17:42 -0700415 # Ask portage to build a dependency graph. with the options we specified
416 # above.
David Jamesfcb70ef2011-02-02 16:02:30 -0800417 params = create_depgraph_params(emerge_opts, emerge.action)
David Jamesbf1e3442011-05-28 07:44:20 -0700418 success, depgraph, favorites = backtrack_depgraph(
David James386ccd12011-05-04 20:17:42 -0700419 emerge.settings, emerge.trees, emerge_opts, params, emerge.action,
420 packages, emerge.spinner)
421 emerge.depgraph = depgraph
David Jamesfcb70ef2011-02-02 16:02:30 -0800422
David James386ccd12011-05-04 20:17:42 -0700423 # Is it impossible to honor the user's request? Bail!
424 if not success:
425 depgraph.display_problems()
426 sys.exit(1)
David Jamesfcb70ef2011-02-02 16:02:30 -0800427
428 emerge.depgraph = depgraph
David Jamesbf1e3442011-05-28 07:44:20 -0700429 emerge.favorites = favorites
David Jamesfcb70ef2011-02-02 16:02:30 -0800430
David Jamesdeebd692011-05-09 17:02:52 -0700431 # Prime and flush emerge caches.
432 root = emerge.settings["ROOT"]
433 vardb = emerge.trees[root]["vartree"].dbapi
David James0bdc5de2011-05-12 16:22:26 -0700434 if "--pretend" not in emerge.opts:
435 vardb.counter_tick()
David Jamesdeebd692011-05-09 17:02:52 -0700436 vardb.flush_cache()
437
David James386ccd12011-05-04 20:17:42 -0700438 def GenDependencyTree(self):
David Jamesfcb70ef2011-02-02 16:02:30 -0800439 """Get dependency tree info from emerge.
440
David Jamesfcb70ef2011-02-02 16:02:30 -0800441 Returns:
442 Dependency tree
443 """
444 start = time.time()
445
446 emerge = self.emerge
447
448 # Create a list of packages to merge
449 packages = set(emerge.cmdline_packages[:])
David Jamesfcb70ef2011-02-02 16:02:30 -0800450
451 # Tell emerge to be quiet. We print plenty of info ourselves so we don't
452 # need any extra output from portage.
453 portage.util.noiselimit = -1
454
455 # My favorite feature: The silent spinner. It doesn't spin. Ever.
456 # I'd disable the colors by default too, but they look kind of cool.
457 emerge.spinner = stdout_spinner()
458 emerge.spinner.update = emerge.spinner.update_quiet
459
460 if "--quiet" not in emerge.opts:
Mike Frysinger383367e2014-09-16 15:06:17 -0400461 print("Calculating deps...")
David Jamesfcb70ef2011-02-02 16:02:30 -0800462
463 self.CreateDepgraph(emerge, packages)
464 depgraph = emerge.depgraph
465
466 # Build our own tree from the emerge digraph.
467 deps_tree = {}
Don Garrett25f309a2014-03-19 14:02:12 -0700468 # pylint: disable=W0212
David Jamesfcb70ef2011-02-02 16:02:30 -0800469 digraph = depgraph._dynamic_config.digraph
David James3f778802011-08-25 19:31:45 -0700470 root = emerge.settings["ROOT"]
Bertrand SIMONNETa15b5072014-10-23 15:27:52 -0700471 final_db = depgraph._dynamic_config._filtered_trees[root]['graph_db']
David Jamesfcb70ef2011-02-02 16:02:30 -0800472 for node, node_deps in digraph.nodes.items():
473 # Calculate dependency packages that need to be installed first. Each
474 # child on the digraph is a dependency. The "operation" field specifies
475 # what we're doing (e.g. merge, uninstall, etc.). The "priorities" array
476 # contains the type of dependency (e.g. build, runtime, runtime_post,
477 # etc.)
478 #
David Jamesfcb70ef2011-02-02 16:02:30 -0800479 # Portage refers to the identifiers for packages as a CPV. This acronym
480 # stands for Component/Path/Version.
481 #
482 # Here's an example CPV: chromeos-base/power_manager-0.0.1-r1
483 # Split up, this CPV would be:
484 # C -- Component: chromeos-base
485 # P -- Path: power_manager
486 # V -- Version: 0.0.1-r1
487 #
488 # We just refer to CPVs as packages here because it's easier.
489 deps = {}
490 for child, priorities in node_deps[0].items():
David James3f778802011-08-25 19:31:45 -0700491 if isinstance(child, Package) and child.root == root:
492 cpv = str(child.cpv)
493 action = str(child.operation)
494
495 # If we're uninstalling a package, check whether Portage is
496 # installing a replacement. If so, just depend on the installation
497 # of the new package, because the old package will automatically
498 # be uninstalled at that time.
499 if action == "uninstall":
500 for pkg in final_db.match_pkgs(child.slot_atom):
501 cpv = str(pkg.cpv)
502 action = "merge"
503 break
504
505 deps[cpv] = dict(action=action,
506 deptypes=[str(x) for x in priorities],
507 deps={})
David Jamesfcb70ef2011-02-02 16:02:30 -0800508
509 # We've built our list of deps, so we can add our package to the tree.
David James3f778802011-08-25 19:31:45 -0700510 if isinstance(node, Package) and node.root == root:
David Jamesfcb70ef2011-02-02 16:02:30 -0800511 deps_tree[str(node.cpv)] = dict(action=str(node.operation),
512 deps=deps)
513
David Jamesfcb70ef2011-02-02 16:02:30 -0800514 # Ask portage for its install plan, so that we can only throw out
David James386ccd12011-05-04 20:17:42 -0700515 # dependencies that portage throws out.
David Jamesfcb70ef2011-02-02 16:02:30 -0800516 deps_info = {}
517 for pkg in depgraph.altlist():
518 if isinstance(pkg, Package):
David James3f778802011-08-25 19:31:45 -0700519 assert pkg.root == root
David Jamesfcb70ef2011-02-02 16:02:30 -0800520 self.package_db[pkg.cpv] = pkg
521
David Jamesfcb70ef2011-02-02 16:02:30 -0800522 # Save off info about the package
David James386ccd12011-05-04 20:17:42 -0700523 deps_info[str(pkg.cpv)] = {"idx": len(deps_info)}
David Jamesfcb70ef2011-02-02 16:02:30 -0800524
525 seconds = time.time() - start
526 if "--quiet" not in emerge.opts:
Mike Frysinger383367e2014-09-16 15:06:17 -0400527 print("Deps calculated in %dm%.1fs" % (seconds / 60, seconds % 60))
David Jamesfcb70ef2011-02-02 16:02:30 -0800528
529 return deps_tree, deps_info
530
531 def PrintTree(self, deps, depth=""):
532 """Print the deps we have seen in the emerge output.
533
534 Args:
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400535 deps: Dependency tree structure.
536 depth: Allows printing the tree recursively, with indentation.
David Jamesfcb70ef2011-02-02 16:02:30 -0800537 """
538 for entry in sorted(deps):
539 action = deps[entry]["action"]
Mike Frysinger383367e2014-09-16 15:06:17 -0400540 print("%s %s (%s)" % (depth, entry, action))
David Jamesfcb70ef2011-02-02 16:02:30 -0800541 self.PrintTree(deps[entry]["deps"], depth=depth + " ")
542
David James386ccd12011-05-04 20:17:42 -0700543 def GenDependencyGraph(self, deps_tree, deps_info):
David Jamesfcb70ef2011-02-02 16:02:30 -0800544 """Generate a doubly linked dependency graph.
545
546 Args:
547 deps_tree: Dependency tree structure.
548 deps_info: More details on the dependencies.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500549
David Jamesfcb70ef2011-02-02 16:02:30 -0800550 Returns:
551 Deps graph in the form of a dict of packages, with each package
552 specifying a "needs" list and "provides" list.
553 """
554 emerge = self.emerge
David Jamesfcb70ef2011-02-02 16:02:30 -0800555
David Jamesfcb70ef2011-02-02 16:02:30 -0800556 # deps_map is the actual dependency graph.
557 #
558 # Each package specifies a "needs" list and a "provides" list. The "needs"
559 # list indicates which packages we depend on. The "provides" list
560 # indicates the reverse dependencies -- what packages need us.
561 #
562 # We also provide some other information in the dependency graph:
563 # - action: What we're planning on doing with this package. Generally,
564 # "merge", "nomerge", or "uninstall"
David Jamesfcb70ef2011-02-02 16:02:30 -0800565 deps_map = {}
566
567 def ReverseTree(packages):
568 """Convert tree to digraph.
569
570 Take the tree of package -> requirements and reverse it to a digraph of
571 buildable packages -> packages they unblock.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500572
David Jamesfcb70ef2011-02-02 16:02:30 -0800573 Args:
574 packages: Tree(s) of dependencies.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500575
David Jamesfcb70ef2011-02-02 16:02:30 -0800576 Returns:
577 Unsanitized digraph.
578 """
David James8c7e5e32011-06-28 11:26:03 -0700579 binpkg_phases = set(["setup", "preinst", "postinst"])
David Jamese5e1c0a2014-09-29 17:19:41 -0700580 needed_dep_types = set(["blocker", "buildtime", "buildtime_slot_op",
581 "runtime", "runtime_slot_op"])
582 ignored_dep_types = set(["ignored", "optional", "runtime_post", "soft"])
583 all_dep_types = ignored_dep_types | needed_dep_types
David Jamesfcb70ef2011-02-02 16:02:30 -0800584 for pkg in packages:
585
586 # Create an entry for the package
587 action = packages[pkg]["action"]
David James8c7e5e32011-06-28 11:26:03 -0700588 default_pkg = {"needs": {}, "provides": set(), "action": action,
589 "nodeps": False, "binary": False}
David Jamesfcb70ef2011-02-02 16:02:30 -0800590 this_pkg = deps_map.setdefault(pkg, default_pkg)
591
David James8c7e5e32011-06-28 11:26:03 -0700592 if pkg in deps_info:
593 this_pkg["idx"] = deps_info[pkg]["idx"]
594
595 # If a package doesn't have any defined phases that might use the
596 # dependent packages (i.e. pkg_setup, pkg_preinst, or pkg_postinst),
597 # we can install this package before its deps are ready.
598 emerge_pkg = self.package_db.get(pkg)
599 if emerge_pkg and emerge_pkg.type_name == "binary":
600 this_pkg["binary"] = True
Mike Frysinger66652ec2014-04-24 11:42:25 -0400601 defined_phases = emerge_pkg.defined_phases
David James8c7e5e32011-06-28 11:26:03 -0700602 defined_binpkg_phases = binpkg_phases.intersection(defined_phases)
603 if not defined_binpkg_phases:
604 this_pkg["nodeps"] = True
605
David Jamesfcb70ef2011-02-02 16:02:30 -0800606 # Create entries for dependencies of this package first.
607 ReverseTree(packages[pkg]["deps"])
608
609 # Add dependencies to this package.
610 for dep, dep_item in packages[pkg]["deps"].iteritems():
David James8c7e5e32011-06-28 11:26:03 -0700611 # We only need to enforce strict ordering of dependencies if the
David James3f778802011-08-25 19:31:45 -0700612 # dependency is a blocker, or is a buildtime or runtime dependency.
613 # (I.e., ignored, optional, and runtime_post dependencies don't
614 # depend on ordering.)
David James8c7e5e32011-06-28 11:26:03 -0700615 dep_types = dep_item["deptypes"]
616 if needed_dep_types.intersection(dep_types):
617 deps_map[dep]["provides"].add(pkg)
618 this_pkg["needs"][dep] = "/".join(dep_types)
David Jamesfcb70ef2011-02-02 16:02:30 -0800619
David Jamese5e1c0a2014-09-29 17:19:41 -0700620 # Verify we processed all appropriate dependency types.
621 unknown_dep_types = set(dep_types) - all_dep_types
622 if unknown_dep_types:
623 print("Unknown dependency types found:")
624 print(" %s -> %s (%s)" % (pkg, dep, "/".join(unknown_dep_types)))
625 sys.exit(1)
626
David James3f778802011-08-25 19:31:45 -0700627 # If there's a blocker, Portage may need to move files from one
628 # package to another, which requires editing the CONTENTS files of
629 # both packages. To avoid race conditions while editing this file,
630 # the two packages must not be installed in parallel, so we can't
631 # safely ignore dependencies. See http://crosbug.com/19328
632 if "blocker" in dep_types:
633 this_pkg["nodeps"] = False
634
David Jamesfcb70ef2011-02-02 16:02:30 -0800635 def FindCycles():
636 """Find cycles in the dependency tree.
637
638 Returns:
639 A dict mapping cyclic packages to a dict of the deps that cause
640 cycles. For each dep that causes cycles, it returns an example
641 traversal of the graph that shows the cycle.
642 """
643
644 def FindCyclesAtNode(pkg, cycles, unresolved, resolved):
645 """Find cycles in cyclic dependencies starting at specified package.
646
647 Args:
648 pkg: Package identifier.
649 cycles: A dict mapping cyclic packages to a dict of the deps that
650 cause cycles. For each dep that causes cycles, it returns an
651 example traversal of the graph that shows the cycle.
652 unresolved: Nodes that have been visited but are not fully processed.
653 resolved: Nodes that have been visited and are fully processed.
654 """
655 pkg_cycles = cycles.get(pkg)
656 if pkg in resolved and not pkg_cycles:
657 # If we already looked at this package, and found no cyclic
658 # dependencies, we can stop now.
659 return
660 unresolved.append(pkg)
661 for dep in deps_map[pkg]["needs"]:
662 if dep in unresolved:
663 idx = unresolved.index(dep)
664 mycycle = unresolved[idx:] + [dep]
David James321490a2012-12-17 12:05:56 -0800665 for i in xrange(len(mycycle) - 1):
David Jamesfcb70ef2011-02-02 16:02:30 -0800666 pkg1, pkg2 = mycycle[i], mycycle[i+1]
667 cycles.setdefault(pkg1, {}).setdefault(pkg2, mycycle)
668 elif not pkg_cycles or dep not in pkg_cycles:
669 # Looks like we haven't seen this edge before.
670 FindCyclesAtNode(dep, cycles, unresolved, resolved)
671 unresolved.pop()
672 resolved.add(pkg)
673
674 cycles, unresolved, resolved = {}, [], set()
675 for pkg in deps_map:
676 FindCyclesAtNode(pkg, cycles, unresolved, resolved)
677 return cycles
678
David James386ccd12011-05-04 20:17:42 -0700679 def RemoveUnusedPackages():
David Jamesfcb70ef2011-02-02 16:02:30 -0800680 """Remove installed packages, propagating dependencies."""
David Jamesfcb70ef2011-02-02 16:02:30 -0800681 # Schedule packages that aren't on the install list for removal
682 rm_pkgs = set(deps_map.keys()) - set(deps_info.keys())
683
David Jamesfcb70ef2011-02-02 16:02:30 -0800684 # Remove the packages we don't want, simplifying the graph and making
685 # it easier for us to crack cycles.
686 for pkg in sorted(rm_pkgs):
687 this_pkg = deps_map[pkg]
688 needs = this_pkg["needs"]
689 provides = this_pkg["provides"]
690 for dep in needs:
691 dep_provides = deps_map[dep]["provides"]
692 dep_provides.update(provides)
693 dep_provides.discard(pkg)
694 dep_provides.discard(dep)
695 for target in provides:
696 target_needs = deps_map[target]["needs"]
697 target_needs.update(needs)
698 target_needs.pop(pkg, None)
699 target_needs.pop(target, None)
700 del deps_map[pkg]
701
702 def PrintCycleBreak(basedep, dep, mycycle):
703 """Print details about a cycle that we are planning on breaking.
704
Mike Frysinger02e1e072013-11-10 22:11:34 -0500705 We are breaking a cycle where dep needs basedep. mycycle is an
706 example cycle which contains dep -> basedep.
707 """
David Jamesfcb70ef2011-02-02 16:02:30 -0800708
David Jamesfcb70ef2011-02-02 16:02:30 -0800709 needs = deps_map[dep]["needs"]
710 depinfo = needs.get(basedep, "deleted")
David Jamesfcb70ef2011-02-02 16:02:30 -0800711
David James3f778802011-08-25 19:31:45 -0700712 # It's OK to swap install order for blockers, as long as the two
713 # packages aren't installed in parallel. If there is a cycle, then
714 # we know the packages depend on each other already, so we can drop the
715 # blocker safely without printing a warning.
716 if depinfo == "blocker":
717 return
718
David Jamesfcb70ef2011-02-02 16:02:30 -0800719 # Notify the user that we're breaking a cycle.
Mike Frysinger383367e2014-09-16 15:06:17 -0400720 print("Breaking %s -> %s (%s)" % (dep, basedep, depinfo))
David Jamesfcb70ef2011-02-02 16:02:30 -0800721
722 # Show cycle.
David James321490a2012-12-17 12:05:56 -0800723 for i in xrange(len(mycycle) - 1):
David Jamesfcb70ef2011-02-02 16:02:30 -0800724 pkg1, pkg2 = mycycle[i], mycycle[i+1]
725 needs = deps_map[pkg1]["needs"]
726 depinfo = needs.get(pkg2, "deleted")
727 if pkg1 == dep and pkg2 == basedep:
728 depinfo = depinfo + ", deleting"
Mike Frysinger383367e2014-09-16 15:06:17 -0400729 print(" %s -> %s (%s)" % (pkg1, pkg2, depinfo))
David Jamesfcb70ef2011-02-02 16:02:30 -0800730
731 def SanitizeTree():
732 """Remove circular dependencies.
733
734 We prune all dependencies involved in cycles that go against the emerge
735 ordering. This has a nice property: we're guaranteed to merge
736 dependencies in the same order that portage does.
737
738 Because we don't treat any dependencies as "soft" unless they're killed
739 by a cycle, we pay attention to a larger number of dependencies when
740 merging. This hurts performance a bit, but helps reliability.
741 """
742 start = time.time()
743 cycles = FindCycles()
744 while cycles:
745 for dep, mycycles in cycles.iteritems():
746 for basedep, mycycle in mycycles.iteritems():
747 if deps_info[basedep]["idx"] >= deps_info[dep]["idx"]:
Matt Tennant08797302011-10-17 16:18:45 -0700748 if "--quiet" not in emerge.opts:
749 PrintCycleBreak(basedep, dep, mycycle)
David Jamesfcb70ef2011-02-02 16:02:30 -0800750 del deps_map[dep]["needs"][basedep]
751 deps_map[basedep]["provides"].remove(dep)
752 cycles = FindCycles()
753 seconds = time.time() - start
754 if "--quiet" not in emerge.opts and seconds >= 0.1:
Mike Frysinger383367e2014-09-16 15:06:17 -0400755 print("Tree sanitized in %dm%.1fs" % (seconds / 60, seconds % 60))
David Jamesfcb70ef2011-02-02 16:02:30 -0800756
David James8c7e5e32011-06-28 11:26:03 -0700757 def FindRecursiveProvides(pkg, seen):
758 """Find all nodes that require a particular package.
759
760 Assumes that graph is acyclic.
761
762 Args:
763 pkg: Package identifier.
764 seen: Nodes that have been visited so far.
765 """
766 if pkg in seen:
767 return
768 seen.add(pkg)
769 info = deps_map[pkg]
770 info["tprovides"] = info["provides"].copy()
771 for dep in info["provides"]:
772 FindRecursiveProvides(dep, seen)
773 info["tprovides"].update(deps_map[dep]["tprovides"])
774
David Jamesa22906f2011-05-04 19:53:26 -0700775 ReverseTree(deps_tree)
David Jamesa22906f2011-05-04 19:53:26 -0700776
David James386ccd12011-05-04 20:17:42 -0700777 # We need to remove unused packages so that we can use the dependency
778 # ordering of the install process to show us what cycles to crack.
779 RemoveUnusedPackages()
David Jamesfcb70ef2011-02-02 16:02:30 -0800780 SanitizeTree()
David James8c7e5e32011-06-28 11:26:03 -0700781 seen = set()
782 for pkg in deps_map:
783 FindRecursiveProvides(pkg, seen)
David Jamesfcb70ef2011-02-02 16:02:30 -0800784 return deps_map
785
786 def PrintInstallPlan(self, deps_map):
787 """Print an emerge-style install plan.
788
789 The install plan lists what packages we're installing, in order.
790 It's useful for understanding what parallel_emerge is doing.
791
792 Args:
793 deps_map: The dependency graph.
794 """
795
796 def InstallPlanAtNode(target, deps_map):
797 nodes = []
798 nodes.append(target)
799 for dep in deps_map[target]["provides"]:
800 del deps_map[dep]["needs"][target]
801 if not deps_map[dep]["needs"]:
802 nodes.extend(InstallPlanAtNode(dep, deps_map))
803 return nodes
804
805 deps_map = copy.deepcopy(deps_map)
806 install_plan = []
807 plan = set()
808 for target, info in deps_map.iteritems():
809 if not info["needs"] and target not in plan:
810 for item in InstallPlanAtNode(target, deps_map):
811 plan.add(item)
812 install_plan.append(self.package_db[item])
813
814 for pkg in plan:
815 del deps_map[pkg]
816
817 if deps_map:
Mike Frysinger383367e2014-09-16 15:06:17 -0400818 print("Cyclic dependencies:", " ".join(deps_map))
David Jamesfcb70ef2011-02-02 16:02:30 -0800819 PrintDepsMap(deps_map)
820 sys.exit(1)
821
822 self.emerge.depgraph.display(install_plan)
823
824
825def PrintDepsMap(deps_map):
826 """Print dependency graph, for each package list it's prerequisites."""
827 for i in sorted(deps_map):
Mike Frysinger383367e2014-09-16 15:06:17 -0400828 print("%s: (%s) needs" % (i, deps_map[i]["action"]))
David Jamesfcb70ef2011-02-02 16:02:30 -0800829 needs = deps_map[i]["needs"]
830 for j in sorted(needs):
Mike Frysinger383367e2014-09-16 15:06:17 -0400831 print(" %s" % (j))
David Jamesfcb70ef2011-02-02 16:02:30 -0800832 if not needs:
Mike Frysinger383367e2014-09-16 15:06:17 -0400833 print(" no dependencies")
David Jamesfcb70ef2011-02-02 16:02:30 -0800834
835
836class EmergeJobState(object):
Don Garrett25f309a2014-03-19 14:02:12 -0700837 """Structure describing the EmergeJobState."""
838
David Jamesfcb70ef2011-02-02 16:02:30 -0800839 __slots__ = ["done", "filename", "last_notify_timestamp", "last_output_seek",
840 "last_output_timestamp", "pkgname", "retcode", "start_timestamp",
Thiago Goncalesf4acc422013-07-17 10:26:35 -0700841 "target", "fetch_only", "unpack_only"]
David Jamesfcb70ef2011-02-02 16:02:30 -0800842
843 def __init__(self, target, pkgname, done, filename, start_timestamp,
Thiago Goncalesf4acc422013-07-17 10:26:35 -0700844 retcode=None, fetch_only=False, unpack_only=False):
David Jamesfcb70ef2011-02-02 16:02:30 -0800845
846 # The full name of the target we're building (e.g.
Mike Frysingerfd969312014-04-02 22:16:42 -0400847 # virtual/target-os-1-r60)
David Jamesfcb70ef2011-02-02 16:02:30 -0800848 self.target = target
849
Mike Frysingerfd969312014-04-02 22:16:42 -0400850 # The short name of the target we're building (e.g. target-os-1-r60)
David Jamesfcb70ef2011-02-02 16:02:30 -0800851 self.pkgname = pkgname
852
853 # Whether the job is done. (True if the job is done; false otherwise.)
854 self.done = done
855
856 # The filename where output is currently stored.
857 self.filename = filename
858
859 # The timestamp of the last time we printed the name of the log file. We
860 # print this at the beginning of the job, so this starts at
861 # start_timestamp.
862 self.last_notify_timestamp = start_timestamp
863
864 # The location (in bytes) of the end of the last complete line we printed.
865 # This starts off at zero. We use this to jump to the right place when we
866 # print output from the same ebuild multiple times.
867 self.last_output_seek = 0
868
869 # The timestamp of the last time we printed output. Since we haven't
870 # printed output yet, this starts at zero.
871 self.last_output_timestamp = 0
872
873 # The return code of our job, if the job is actually finished.
874 self.retcode = retcode
875
Brian Harring0be85c62012-03-17 19:52:12 -0700876 # Was this just a fetch job?
877 self.fetch_only = fetch_only
878
David Jamesfcb70ef2011-02-02 16:02:30 -0800879 # The timestamp when our job started.
880 self.start_timestamp = start_timestamp
881
Thiago Goncalesf4acc422013-07-17 10:26:35 -0700882 # No emerge, only unpack packages.
883 self.unpack_only = unpack_only
884
David Jamesfcb70ef2011-02-02 16:02:30 -0800885
David James321490a2012-12-17 12:05:56 -0800886def KillHandler(_signum, _frame):
David James7358d032011-05-19 10:40:03 -0700887 # Kill self and all subprocesses.
888 os.killpg(0, signal.SIGKILL)
889
Mike Frysingercc838832014-05-24 13:10:30 -0400890
David Jamesfcb70ef2011-02-02 16:02:30 -0800891def SetupWorkerSignals():
David James321490a2012-12-17 12:05:56 -0800892 def ExitHandler(_signum, _frame):
David James7358d032011-05-19 10:40:03 -0700893 # Set KILLED flag.
894 KILLED.set()
David James13cead42011-05-18 16:22:01 -0700895
David James7358d032011-05-19 10:40:03 -0700896 # Remove our signal handlers so we don't get called recursively.
897 signal.signal(signal.SIGINT, KillHandler)
898 signal.signal(signal.SIGTERM, KillHandler)
David Jamesfcb70ef2011-02-02 16:02:30 -0800899
900 # Ensure that we exit quietly and cleanly, if possible, when we receive
901 # SIGTERM or SIGINT signals. By default, when the user hits CTRL-C, all
902 # of the child processes will print details about KeyboardInterrupt
903 # exceptions, which isn't very helpful.
904 signal.signal(signal.SIGINT, ExitHandler)
905 signal.signal(signal.SIGTERM, ExitHandler)
906
Mike Frysingerd74fe4a2014-04-24 11:43:38 -0400907
908def EmergeProcess(output, target, *args, **kwargs):
David James1ed3e252011-10-05 20:26:15 -0700909 """Merge a package in a subprocess.
910
911 Args:
David James1ed3e252011-10-05 20:26:15 -0700912 output: Temporary file to write output.
Mike Frysingerd74fe4a2014-04-24 11:43:38 -0400913 target: The package we'll be processing (for display purposes).
David James6b29d052012-11-02 10:27:27 -0700914 *args: Arguments to pass to Scheduler constructor.
915 **kwargs: Keyword arguments to pass to Scheduler constructor.
David James1ed3e252011-10-05 20:26:15 -0700916
917 Returns:
918 The exit code returned by the subprocess.
919 """
920 pid = os.fork()
921 if pid == 0:
922 try:
Mike Frysingerd74fe4a2014-04-24 11:43:38 -0400923 proctitle.settitle('EmergeProcess', target)
924
David James1ed3e252011-10-05 20:26:15 -0700925 # Sanity checks.
Mike Frysingerf02736e2013-11-08 15:27:00 -0500926 if sys.stdout.fileno() != 1:
927 raise Exception("sys.stdout.fileno() != 1")
928 if sys.stderr.fileno() != 2:
929 raise Exception("sys.stderr.fileno() != 2")
David James1ed3e252011-10-05 20:26:15 -0700930
931 # - Redirect 1 (stdout) and 2 (stderr) at our temporary file.
932 # - Redirect 0 to point at sys.stdin. In this case, sys.stdin
933 # points at a file reading os.devnull, because multiprocessing mucks
934 # with sys.stdin.
935 # - Leave the sys.stdin and output filehandles alone.
936 fd_pipes = {0: sys.stdin.fileno(),
937 1: output.fileno(),
938 2: output.fileno(),
939 sys.stdin.fileno(): sys.stdin.fileno(),
940 output.fileno(): output.fileno()}
Mike Frysinger66652ec2014-04-24 11:42:25 -0400941 # pylint: disable=W0212
942 portage.process._setup_pipes(fd_pipes, close_fds=False)
David James1ed3e252011-10-05 20:26:15 -0700943
944 # Portage doesn't like when sys.stdin.fileno() != 0, so point sys.stdin
945 # at the filehandle we just created in _setup_pipes.
946 if sys.stdin.fileno() != 0:
David James6b29d052012-11-02 10:27:27 -0700947 sys.__stdin__ = sys.stdin = os.fdopen(0, "r")
948
949 scheduler = Scheduler(*args, **kwargs)
950
951 # Enable blocker handling even though we're in --nodeps mode. This
952 # allows us to unmerge the blocker after we've merged the replacement.
953 scheduler._opts_ignore_blockers = frozenset()
David James1ed3e252011-10-05 20:26:15 -0700954
955 # Actually do the merge.
956 retval = scheduler.merge()
957
958 # We catch all exceptions here (including SystemExit, KeyboardInterrupt,
959 # etc) so as to ensure that we don't confuse the multiprocessing module,
960 # which expects that all forked children exit with os._exit().
David James321490a2012-12-17 12:05:56 -0800961 # pylint: disable=W0702
David James1ed3e252011-10-05 20:26:15 -0700962 except:
963 traceback.print_exc(file=output)
964 retval = 1
965 sys.stdout.flush()
966 sys.stderr.flush()
967 output.flush()
Don Garrett25f309a2014-03-19 14:02:12 -0700968 # pylint: disable=W0212
David James1ed3e252011-10-05 20:26:15 -0700969 os._exit(retval)
970 else:
971 # Return the exit code of the subprocess.
972 return os.waitpid(pid, 0)[1]
David Jamesfcb70ef2011-02-02 16:02:30 -0800973
Thiago Goncalesf4acc422013-07-17 10:26:35 -0700974
975def UnpackPackage(pkg_state):
976 """Unpacks package described by pkg_state.
977
978 Args:
979 pkg_state: EmergeJobState object describing target.
980
981 Returns:
982 Exit code returned by subprocess.
983 """
984 pkgdir = os.environ.get("PKGDIR",
985 os.path.join(os.environ["SYSROOT"], "packages"))
986 root = os.environ.get("ROOT", os.environ["SYSROOT"])
987 path = os.path.join(pkgdir, pkg_state.target + ".tbz2")
988 comp = cros_build_lib.FindCompressor(cros_build_lib.COMP_BZIP2)
989 cmd = [comp, "-dc"]
990 if comp.endswith("pbzip2"):
991 cmd.append("--ignore-trailing-garbage=1")
992 cmd.append(path)
993
994 result = cros_build_lib.RunCommand(cmd, cwd=root, stdout_to_pipe=True,
995 print_cmd=False, error_code_ok=True)
996
997 # If we were not successful, return now and don't attempt untar.
998 if result.returncode:
999 return result.returncode
1000
1001 cmd = ["sudo", "tar", "-xf", "-", "-C", root]
1002 result = cros_build_lib.RunCommand(cmd, cwd=root, input=result.output,
1003 print_cmd=False, error_code_ok=True)
1004
1005 return result.returncode
1006
1007
1008def EmergeWorker(task_queue, job_queue, emerge, package_db, fetch_only=False,
1009 unpack_only=False):
David Jamesfcb70ef2011-02-02 16:02:30 -08001010 """This worker emerges any packages given to it on the task_queue.
1011
1012 Args:
1013 task_queue: The queue of tasks for this worker to do.
1014 job_queue: The queue of results from the worker.
1015 emerge: An EmergeData() object.
1016 package_db: A dict, mapping package ids to portage Package objects.
Brian Harring0be85c62012-03-17 19:52:12 -07001017 fetch_only: A bool, indicating if we should just fetch the target.
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001018 unpack_only: A bool, indicating if we should just unpack the target.
David Jamesfcb70ef2011-02-02 16:02:30 -08001019
1020 It expects package identifiers to be passed to it via task_queue. When
1021 a task is started, it pushes the (target, filename) to the started_queue.
1022 The output is stored in filename. When a merge starts or finishes, we push
1023 EmergeJobState objects to the job_queue.
1024 """
Mike Frysingerd74fe4a2014-04-24 11:43:38 -04001025 if fetch_only:
1026 mode = 'fetch'
1027 elif unpack_only:
1028 mode = 'unpack'
1029 else:
1030 mode = 'emerge'
1031 proctitle.settitle('EmergeWorker', mode, '[idle]')
David Jamesfcb70ef2011-02-02 16:02:30 -08001032
1033 SetupWorkerSignals()
1034 settings, trees, mtimedb = emerge.settings, emerge.trees, emerge.mtimedb
David Jamesdeebd692011-05-09 17:02:52 -07001035
1036 # Disable flushing of caches to save on I/O.
David James7a1ea4b2011-10-13 15:06:41 -07001037 root = emerge.settings["ROOT"]
1038 vardb = emerge.trees[root]["vartree"].dbapi
Mike Frysingere56debd2014-11-19 01:54:36 -05001039 vardb._flush_cache_enabled = False # pylint: disable=protected-access
Brian Harring0be85c62012-03-17 19:52:12 -07001040 bindb = emerge.trees[root]["bintree"].dbapi
1041 # Might be a set, might be a list, might be None; no clue, just use shallow
1042 # copy to ensure we can roll it back.
Don Garrett25f309a2014-03-19 14:02:12 -07001043 # pylint: disable=W0212
Brian Harring0be85c62012-03-17 19:52:12 -07001044 original_remotepkgs = copy.copy(bindb.bintree._remotepkgs)
David Jamesdeebd692011-05-09 17:02:52 -07001045
David Jamesfcb70ef2011-02-02 16:02:30 -08001046 opts, spinner = emerge.opts, emerge.spinner
1047 opts["--nodeps"] = True
Brian Harring0be85c62012-03-17 19:52:12 -07001048 if fetch_only:
1049 opts["--fetchonly"] = True
1050
David Jamesfcb70ef2011-02-02 16:02:30 -08001051 while True:
1052 # Wait for a new item to show up on the queue. This is a blocking wait,
1053 # so if there's nothing to do, we just sit here.
Brian Harring0be85c62012-03-17 19:52:12 -07001054 pkg_state = task_queue.get()
1055 if pkg_state is None:
David Jamesfcb70ef2011-02-02 16:02:30 -08001056 # If target is None, this means that the main thread wants us to quit.
1057 # The other workers need to exit too, so we'll push the message back on
1058 # to the queue so they'll get it too.
Brian Harring0be85c62012-03-17 19:52:12 -07001059 task_queue.put(None)
David Jamesfcb70ef2011-02-02 16:02:30 -08001060 return
David James7358d032011-05-19 10:40:03 -07001061 if KILLED.is_set():
1062 return
1063
Brian Harring0be85c62012-03-17 19:52:12 -07001064 target = pkg_state.target
Mike Frysingerd74fe4a2014-04-24 11:43:38 -04001065 proctitle.settitle('EmergeWorker', mode, target)
Brian Harring0be85c62012-03-17 19:52:12 -07001066
David Jamesfcb70ef2011-02-02 16:02:30 -08001067 db_pkg = package_db[target]
Brian Harring0be85c62012-03-17 19:52:12 -07001068
1069 if db_pkg.type_name == "binary":
1070 if not fetch_only and pkg_state.fetched_successfully:
1071 # Ensure portage doesn't think our pkg is remote- else it'll force
1072 # a redownload of it (even if the on-disk file is fine). In-memory
1073 # caching basically, implemented dumbly.
1074 bindb.bintree._remotepkgs = None
1075 else:
1076 bindb.bintree_remotepkgs = original_remotepkgs
1077
David Jamesfcb70ef2011-02-02 16:02:30 -08001078 db_pkg.root_config = emerge.root_config
1079 install_list = [db_pkg]
1080 pkgname = db_pkg.pf
1081 output = tempfile.NamedTemporaryFile(prefix=pkgname + "-", delete=False)
David James01b1e0f2012-06-07 17:18:05 -07001082 os.chmod(output.name, 644)
David Jamesfcb70ef2011-02-02 16:02:30 -08001083 start_timestamp = time.time()
Brian Harring0be85c62012-03-17 19:52:12 -07001084 job = EmergeJobState(target, pkgname, False, output.name, start_timestamp,
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001085 fetch_only=fetch_only, unpack_only=unpack_only)
David Jamesfcb70ef2011-02-02 16:02:30 -08001086 job_queue.put(job)
1087 if "--pretend" in opts:
1088 retcode = 0
1089 else:
David Jamesfcb70ef2011-02-02 16:02:30 -08001090 try:
David James386ccd12011-05-04 20:17:42 -07001091 emerge.scheduler_graph.mergelist = install_list
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001092 if unpack_only:
1093 retcode = UnpackPackage(pkg_state)
1094 else:
Mike Frysingerd74fe4a2014-04-24 11:43:38 -04001095 retcode = EmergeProcess(output, target, settings, trees, mtimedb,
1096 opts, spinner, favorites=emerge.favorites,
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001097 graph_config=emerge.scheduler_graph)
David Jamesfcb70ef2011-02-02 16:02:30 -08001098 except Exception:
1099 traceback.print_exc(file=output)
1100 retcode = 1
David James1ed3e252011-10-05 20:26:15 -07001101 output.close()
David Jamesfcb70ef2011-02-02 16:02:30 -08001102
David James7358d032011-05-19 10:40:03 -07001103 if KILLED.is_set():
1104 return
1105
David Jamesfcb70ef2011-02-02 16:02:30 -08001106 job = EmergeJobState(target, pkgname, True, output.name, start_timestamp,
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001107 retcode, fetch_only=fetch_only,
1108 unpack_only=unpack_only)
David Jamesfcb70ef2011-02-02 16:02:30 -08001109 job_queue.put(job)
1110
Mike Frysingerd74fe4a2014-04-24 11:43:38 -04001111 # Set the title back to idle as the multiprocess pool won't destroy us;
1112 # when another job comes up, it'll re-use this process.
1113 proctitle.settitle('EmergeWorker', mode, '[idle]')
1114
David Jamesfcb70ef2011-02-02 16:02:30 -08001115
1116class LinePrinter(object):
1117 """Helper object to print a single line."""
1118
1119 def __init__(self, line):
1120 self.line = line
1121
David James321490a2012-12-17 12:05:56 -08001122 def Print(self, _seek_locations):
Mike Frysinger383367e2014-09-16 15:06:17 -04001123 print(self.line)
David Jamesfcb70ef2011-02-02 16:02:30 -08001124
1125
1126class JobPrinter(object):
1127 """Helper object to print output of a job."""
1128
1129 def __init__(self, job, unlink=False):
1130 """Print output of job.
1131
Mike Frysinger02e1e072013-11-10 22:11:34 -05001132 If unlink is True, unlink the job output file when done.
1133 """
David Jamesfcb70ef2011-02-02 16:02:30 -08001134 self.current_time = time.time()
1135 self.job = job
1136 self.unlink = unlink
1137
1138 def Print(self, seek_locations):
1139
1140 job = self.job
1141
1142 # Calculate how long the job has been running.
1143 seconds = self.current_time - job.start_timestamp
1144
1145 # Note that we've printed out the job so far.
1146 job.last_output_timestamp = self.current_time
1147
1148 # Note that we're starting the job
1149 info = "job %s (%dm%.1fs)" % (job.pkgname, seconds / 60, seconds % 60)
1150 last_output_seek = seek_locations.get(job.filename, 0)
1151 if last_output_seek:
Mike Frysinger383367e2014-09-16 15:06:17 -04001152 print("=== Continue output for %s ===" % info)
David Jamesfcb70ef2011-02-02 16:02:30 -08001153 else:
Mike Frysinger383367e2014-09-16 15:06:17 -04001154 print("=== Start output for %s ===" % info)
David Jamesfcb70ef2011-02-02 16:02:30 -08001155
1156 # Print actual output from job
1157 f = codecs.open(job.filename, encoding='utf-8', errors='replace')
1158 f.seek(last_output_seek)
1159 prefix = job.pkgname + ":"
1160 for line in f:
1161
1162 # Save off our position in the file
1163 if line and line[-1] == "\n":
1164 last_output_seek = f.tell()
1165 line = line[:-1]
1166
1167 # Print our line
Mike Frysinger383367e2014-09-16 15:06:17 -04001168 print(prefix, line.encode('utf-8', 'replace'))
David Jamesfcb70ef2011-02-02 16:02:30 -08001169 f.close()
1170
1171 # Save our last spot in the file so that we don't print out the same
1172 # location twice.
1173 seek_locations[job.filename] = last_output_seek
1174
1175 # Note end of output section
1176 if job.done:
Mike Frysinger383367e2014-09-16 15:06:17 -04001177 print("=== Complete: %s ===" % info)
David Jamesfcb70ef2011-02-02 16:02:30 -08001178 else:
Mike Frysinger383367e2014-09-16 15:06:17 -04001179 print("=== Still running: %s ===" % info)
David Jamesfcb70ef2011-02-02 16:02:30 -08001180
1181 if self.unlink:
1182 os.unlink(job.filename)
1183
1184
1185def PrintWorker(queue):
1186 """A worker that prints stuff to the screen as requested."""
Mike Frysingerd74fe4a2014-04-24 11:43:38 -04001187 proctitle.settitle('PrintWorker')
David Jamesfcb70ef2011-02-02 16:02:30 -08001188
David James321490a2012-12-17 12:05:56 -08001189 def ExitHandler(_signum, _frame):
David James7358d032011-05-19 10:40:03 -07001190 # Set KILLED flag.
1191 KILLED.set()
1192
David Jamesfcb70ef2011-02-02 16:02:30 -08001193 # Switch to default signal handlers so that we'll die after two signals.
David James7358d032011-05-19 10:40:03 -07001194 signal.signal(signal.SIGINT, KillHandler)
1195 signal.signal(signal.SIGTERM, KillHandler)
David Jamesfcb70ef2011-02-02 16:02:30 -08001196
1197 # Don't exit on the first SIGINT / SIGTERM, because the parent worker will
1198 # handle it and tell us when we need to exit.
1199 signal.signal(signal.SIGINT, ExitHandler)
1200 signal.signal(signal.SIGTERM, ExitHandler)
1201
1202 # seek_locations is a map indicating the position we are at in each file.
1203 # It starts off empty, but is set by the various Print jobs as we go along
1204 # to indicate where we left off in each file.
1205 seek_locations = {}
1206 while True:
1207 try:
1208 job = queue.get()
1209 if job:
1210 job.Print(seek_locations)
David Jamesbccf8eb2011-07-27 14:06:06 -07001211 sys.stdout.flush()
David Jamesfcb70ef2011-02-02 16:02:30 -08001212 else:
1213 break
1214 except IOError as ex:
1215 if ex.errno == errno.EINTR:
1216 # Looks like we received a signal. Keep printing.
1217 continue
1218 raise
1219
Brian Harring867e2362012-03-17 04:05:17 -07001220
Brian Harring0be85c62012-03-17 19:52:12 -07001221class TargetState(object):
Don Garrett25f309a2014-03-19 14:02:12 -07001222 """Structure descriting the TargetState."""
Brian Harring867e2362012-03-17 04:05:17 -07001223
Brian Harring0be85c62012-03-17 19:52:12 -07001224 __slots__ = ("target", "info", "score", "prefetched", "fetched_successfully")
Brian Harring867e2362012-03-17 04:05:17 -07001225
David James321490a2012-12-17 12:05:56 -08001226 def __init__(self, target, info):
Brian Harring867e2362012-03-17 04:05:17 -07001227 self.target, self.info = target, info
Brian Harring0be85c62012-03-17 19:52:12 -07001228 self.fetched_successfully = False
1229 self.prefetched = False
David James321490a2012-12-17 12:05:56 -08001230 self.score = None
Brian Harring867e2362012-03-17 04:05:17 -07001231 self.update_score()
1232
1233 def __cmp__(self, other):
1234 return cmp(self.score, other.score)
1235
1236 def update_score(self):
1237 self.score = (
1238 -len(self.info["tprovides"]),
Brian Harring0be85c62012-03-17 19:52:12 -07001239 len(self.info["needs"]),
Brian Harring11c5eeb2012-03-18 11:02:39 -07001240 not self.info["binary"],
Brian Harring867e2362012-03-17 04:05:17 -07001241 -len(self.info["provides"]),
1242 self.info["idx"],
1243 self.target,
1244 )
1245
1246
1247class ScoredHeap(object):
Don Garrett25f309a2014-03-19 14:02:12 -07001248 """Implementation of a general purpose scored heap."""
Brian Harring867e2362012-03-17 04:05:17 -07001249
Brian Harring0be85c62012-03-17 19:52:12 -07001250 __slots__ = ("heap", "_heap_set")
1251
Brian Harring867e2362012-03-17 04:05:17 -07001252 def __init__(self, initial=()):
Brian Harring0be85c62012-03-17 19:52:12 -07001253 self.heap = list()
1254 self._heap_set = set()
1255 if initial:
1256 self.multi_put(initial)
Brian Harring867e2362012-03-17 04:05:17 -07001257
1258 def get(self):
Brian Harring0be85c62012-03-17 19:52:12 -07001259 item = heapq.heappop(self.heap)
1260 self._heap_set.remove(item.target)
1261 return item
Brian Harring867e2362012-03-17 04:05:17 -07001262
Brian Harring0be85c62012-03-17 19:52:12 -07001263 def put(self, item):
1264 if not isinstance(item, TargetState):
1265 raise ValueError("Item %r isn't a TargetState" % (item,))
1266 heapq.heappush(self.heap, item)
1267 self._heap_set.add(item.target)
Brian Harring867e2362012-03-17 04:05:17 -07001268
Brian Harring0be85c62012-03-17 19:52:12 -07001269 def multi_put(self, sequence):
1270 sequence = list(sequence)
1271 self.heap.extend(sequence)
1272 self._heap_set.update(x.target for x in sequence)
Brian Harring867e2362012-03-17 04:05:17 -07001273 self.sort()
1274
David James5c9996d2012-03-24 10:50:46 -07001275 def sort(self):
1276 heapq.heapify(self.heap)
1277
Brian Harring0be85c62012-03-17 19:52:12 -07001278 def __contains__(self, target):
1279 return target in self._heap_set
1280
1281 def __nonzero__(self):
1282 return bool(self.heap)
1283
Brian Harring867e2362012-03-17 04:05:17 -07001284 def __len__(self):
1285 return len(self.heap)
1286
1287
David Jamesfcb70ef2011-02-02 16:02:30 -08001288class EmergeQueue(object):
1289 """Class to schedule emerge jobs according to a dependency graph."""
1290
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001291 def __init__(self, deps_map, emerge, package_db, show_output, unpack_only,
1292 max_retries):
David Jamesfcb70ef2011-02-02 16:02:30 -08001293 # Store the dependency graph.
1294 self._deps_map = deps_map
Brian Harring0be85c62012-03-17 19:52:12 -07001295 self._state_map = {}
David Jamesfcb70ef2011-02-02 16:02:30 -08001296 # Initialize the running queue to empty
Brian Harring0be85c62012-03-17 19:52:12 -07001297 self._build_jobs = {}
1298 self._build_ready = ScoredHeap()
1299 self._fetch_jobs = {}
1300 self._fetch_ready = ScoredHeap()
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001301 self._unpack_jobs = {}
1302 self._unpack_ready = ScoredHeap()
David Jamesfcb70ef2011-02-02 16:02:30 -08001303 # List of total package installs represented in deps_map.
1304 install_jobs = [x for x in deps_map if deps_map[x]["action"] == "merge"]
1305 self._total_jobs = len(install_jobs)
1306 self._show_output = show_output
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001307 self._unpack_only = unpack_only
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001308 self._max_retries = max_retries
David Jamesfcb70ef2011-02-02 16:02:30 -08001309
1310 if "--pretend" in emerge.opts:
Mike Frysinger383367e2014-09-16 15:06:17 -04001311 print("Skipping merge because of --pretend mode.")
David Jamesfcb70ef2011-02-02 16:02:30 -08001312 sys.exit(0)
1313
David Jamesaaf49e42014-04-24 09:40:05 -07001314 # Set up a session so we can easily terminate all children.
1315 self._SetupSession()
David James7358d032011-05-19 10:40:03 -07001316
David Jamesfcb70ef2011-02-02 16:02:30 -08001317 # Setup scheduler graph object. This is used by the child processes
1318 # to help schedule jobs.
1319 emerge.scheduler_graph = emerge.depgraph.schedulerGraph()
1320
1321 # Calculate how many jobs we can run in parallel. We don't want to pass
1322 # the --jobs flag over to emerge itself, because that'll tell emerge to
1323 # hide its output, and said output is quite useful for debugging hung
1324 # jobs.
1325 procs = min(self._total_jobs,
1326 emerge.opts.pop("--jobs", multiprocessing.cpu_count()))
Nam T. Nguyenf7098b32015-05-08 11:04:48 -07001327 self._build_procs = self._unpack_procs = max(1, procs)
1328 # Fetch is IO bound, we can use more processes.
1329 self._fetch_procs = max(4, procs)
David James8c7e5e32011-06-28 11:26:03 -07001330 self._load_avg = emerge.opts.pop("--load-average", None)
David Jamesfcb70ef2011-02-02 16:02:30 -08001331 self._job_queue = multiprocessing.Queue()
1332 self._print_queue = multiprocessing.Queue()
Brian Harring0be85c62012-03-17 19:52:12 -07001333
1334 self._fetch_queue = multiprocessing.Queue()
1335 args = (self._fetch_queue, self._job_queue, emerge, package_db, True)
1336 self._fetch_pool = multiprocessing.Pool(self._fetch_procs, EmergeWorker,
1337 args)
1338
1339 self._build_queue = multiprocessing.Queue()
1340 args = (self._build_queue, self._job_queue, emerge, package_db)
1341 self._build_pool = multiprocessing.Pool(self._build_procs, EmergeWorker,
1342 args)
1343
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001344 if self._unpack_only:
1345 # Unpack pool only required on unpack_only jobs.
1346 self._unpack_queue = multiprocessing.Queue()
1347 args = (self._unpack_queue, self._job_queue, emerge, package_db, False,
1348 True)
1349 self._unpack_pool = multiprocessing.Pool(self._unpack_procs, EmergeWorker,
1350 args)
1351
David Jamesfcb70ef2011-02-02 16:02:30 -08001352 self._print_worker = multiprocessing.Process(target=PrintWorker,
1353 args=[self._print_queue])
1354 self._print_worker.start()
1355
1356 # Initialize the failed queue to empty.
1357 self._retry_queue = []
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001358 self._failed_count = dict()
David Jamesfcb70ef2011-02-02 16:02:30 -08001359
David Jamesfcb70ef2011-02-02 16:02:30 -08001360 # Setup an exit handler so that we print nice messages if we are
1361 # terminated.
1362 self._SetupExitHandler()
1363
1364 # Schedule our jobs.
Brian Harring0be85c62012-03-17 19:52:12 -07001365 self._state_map.update(
1366 (pkg, TargetState(pkg, data)) for pkg, data in deps_map.iteritems())
1367 self._fetch_ready.multi_put(self._state_map.itervalues())
David Jamesfcb70ef2011-02-02 16:02:30 -08001368
David Jamesaaf49e42014-04-24 09:40:05 -07001369 def _SetupSession(self):
1370 """Set up a session so we can easily terminate all children."""
1371 # When we call os.setsid(), this sets up a session / process group for this
1372 # process and all children. These session groups are needed so that we can
1373 # easily kill all children (including processes launched by emerge) before
1374 # we exit.
1375 #
1376 # One unfortunate side effect of os.setsid() is that it blocks CTRL-C from
1377 # being received. To work around this, we only call os.setsid() in a forked
1378 # process, so that the parent can still watch for CTRL-C. The parent will
1379 # just sit around, watching for signals and propagating them to the child,
1380 # until the child exits.
1381 #
1382 # TODO(davidjames): It would be nice if we could replace this with cgroups.
1383 pid = os.fork()
1384 if pid == 0:
1385 os.setsid()
1386 else:
Mike Frysingerd74fe4a2014-04-24 11:43:38 -04001387 proctitle.settitle('SessionManager')
1388
David Jamesaaf49e42014-04-24 09:40:05 -07001389 def PropagateToChildren(signum, _frame):
1390 # Just propagate the signals down to the child. We'll exit when the
1391 # child does.
1392 try:
1393 os.kill(pid, signum)
1394 except OSError as ex:
1395 if ex.errno != errno.ESRCH:
1396 raise
1397 signal.signal(signal.SIGINT, PropagateToChildren)
1398 signal.signal(signal.SIGTERM, PropagateToChildren)
1399
1400 def StopGroup(_signum, _frame):
1401 # When we get stopped, stop the children.
1402 try:
1403 os.killpg(pid, signal.SIGSTOP)
1404 os.kill(0, signal.SIGSTOP)
1405 except OSError as ex:
1406 if ex.errno != errno.ESRCH:
1407 raise
1408 signal.signal(signal.SIGTSTP, StopGroup)
1409
1410 def ContinueGroup(_signum, _frame):
1411 # Launch the children again after being stopped.
1412 try:
1413 os.killpg(pid, signal.SIGCONT)
1414 except OSError as ex:
1415 if ex.errno != errno.ESRCH:
1416 raise
1417 signal.signal(signal.SIGCONT, ContinueGroup)
1418
1419 # Loop until the children exit. We exit with os._exit to be sure we
1420 # don't run any finalizers (those will be run by the child process.)
1421 # pylint: disable=W0212
1422 while True:
1423 try:
1424 # Wait for the process to exit. When it does, exit with the return
1425 # value of the subprocess.
Mike Frysingere2d8f0d2014-11-01 13:09:26 -04001426 os._exit(process_util.GetExitStatus(os.waitpid(pid, 0)[1]))
David Jamesaaf49e42014-04-24 09:40:05 -07001427 except OSError as ex:
1428 if ex.errno == errno.EINTR:
1429 continue
1430 traceback.print_exc()
1431 os._exit(1)
1432 except BaseException:
1433 traceback.print_exc()
1434 os._exit(1)
1435
David Jamesfcb70ef2011-02-02 16:02:30 -08001436 def _SetupExitHandler(self):
1437
David James321490a2012-12-17 12:05:56 -08001438 def ExitHandler(signum, _frame):
David James7358d032011-05-19 10:40:03 -07001439 # Set KILLED flag.
1440 KILLED.set()
David Jamesfcb70ef2011-02-02 16:02:30 -08001441
1442 # Kill our signal handlers so we don't get called recursively
David James7358d032011-05-19 10:40:03 -07001443 signal.signal(signal.SIGINT, KillHandler)
1444 signal.signal(signal.SIGTERM, KillHandler)
David Jamesfcb70ef2011-02-02 16:02:30 -08001445
1446 # Print our current job status
Brian Harring0be85c62012-03-17 19:52:12 -07001447 for job in self._build_jobs.itervalues():
David Jamesfcb70ef2011-02-02 16:02:30 -08001448 if job:
1449 self._print_queue.put(JobPrinter(job, unlink=True))
1450
1451 # Notify the user that we are exiting
1452 self._Print("Exiting on signal %s" % signum)
David James7358d032011-05-19 10:40:03 -07001453 self._print_queue.put(None)
1454 self._print_worker.join()
David Jamesfcb70ef2011-02-02 16:02:30 -08001455
1456 # Kill child threads, then exit.
David James7358d032011-05-19 10:40:03 -07001457 os.killpg(0, signal.SIGKILL)
David Jamesfcb70ef2011-02-02 16:02:30 -08001458 sys.exit(1)
1459
1460 # Print out job status when we are killed
1461 signal.signal(signal.SIGINT, ExitHandler)
1462 signal.signal(signal.SIGTERM, ExitHandler)
1463
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001464 def _ScheduleUnpack(self, pkg_state):
1465 self._unpack_jobs[pkg_state.target] = None
1466 self._unpack_queue.put(pkg_state)
1467
Brian Harring0be85c62012-03-17 19:52:12 -07001468 def _Schedule(self, pkg_state):
David Jamesfcb70ef2011-02-02 16:02:30 -08001469 # We maintain a tree of all deps, if this doesn't need
David James8c7e5e32011-06-28 11:26:03 -07001470 # to be installed just free up its children and continue.
David Jamesfcb70ef2011-02-02 16:02:30 -08001471 # It is possible to reinstall deps of deps, without reinstalling
1472 # first level deps, like so:
Mike Frysingerfd969312014-04-02 22:16:42 -04001473 # virtual/target-os (merge) -> eselect (nomerge) -> python (merge)
Brian Harring0be85c62012-03-17 19:52:12 -07001474 this_pkg = pkg_state.info
1475 target = pkg_state.target
1476 if pkg_state.info is not None:
1477 if this_pkg["action"] == "nomerge":
1478 self._Finish(target)
1479 elif target not in self._build_jobs:
1480 # Kick off the build if it's marked to be built.
1481 self._build_jobs[target] = None
1482 self._build_queue.put(pkg_state)
1483 return True
David Jamesfcb70ef2011-02-02 16:02:30 -08001484
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001485 def _ScheduleLoop(self, unpack_only=False):
1486 if unpack_only:
1487 ready_queue = self._unpack_ready
1488 jobs_queue = self._unpack_jobs
1489 procs = self._unpack_procs
1490 else:
1491 ready_queue = self._build_ready
1492 jobs_queue = self._build_jobs
1493 procs = self._build_procs
1494
David James8c7e5e32011-06-28 11:26:03 -07001495 # If the current load exceeds our desired load average, don't schedule
1496 # more than one job.
1497 if self._load_avg and os.getloadavg()[0] > self._load_avg:
1498 needed_jobs = 1
1499 else:
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001500 needed_jobs = procs
David James8c7e5e32011-06-28 11:26:03 -07001501
1502 # Schedule more jobs.
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001503 while ready_queue and len(jobs_queue) < needed_jobs:
1504 state = ready_queue.get()
1505 if unpack_only:
1506 self._ScheduleUnpack(state)
1507 else:
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001508 if state.target not in self._failed_count:
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001509 self._Schedule(state)
David Jamesfcb70ef2011-02-02 16:02:30 -08001510
1511 def _Print(self, line):
1512 """Print a single line."""
1513 self._print_queue.put(LinePrinter(line))
1514
1515 def _Status(self):
1516 """Print status."""
1517 current_time = time.time()
1518 no_output = True
1519
1520 # Print interim output every minute if --show-output is used. Otherwise,
1521 # print notifications about running packages every 2 minutes, and print
1522 # full output for jobs that have been running for 60 minutes or more.
1523 if self._show_output:
1524 interval = 60
1525 notify_interval = 0
1526 else:
1527 interval = 60 * 60
1528 notify_interval = 60 * 2
David James321490a2012-12-17 12:05:56 -08001529 for job in self._build_jobs.itervalues():
David Jamesfcb70ef2011-02-02 16:02:30 -08001530 if job:
1531 last_timestamp = max(job.start_timestamp, job.last_output_timestamp)
1532 if last_timestamp + interval < current_time:
1533 self._print_queue.put(JobPrinter(job))
1534 job.last_output_timestamp = current_time
1535 no_output = False
1536 elif (notify_interval and
1537 job.last_notify_timestamp + notify_interval < current_time):
1538 job_seconds = current_time - job.start_timestamp
1539 args = (job.pkgname, job_seconds / 60, job_seconds % 60, job.filename)
1540 info = "Still building %s (%dm%.1fs). Logs in %s" % args
1541 job.last_notify_timestamp = current_time
1542 self._Print(info)
1543 no_output = False
1544
1545 # If we haven't printed any messages yet, print a general status message
1546 # here.
1547 if no_output:
1548 seconds = current_time - GLOBAL_START
Brian Harring0be85c62012-03-17 19:52:12 -07001549 fjobs, fready = len(self._fetch_jobs), len(self._fetch_ready)
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001550 ujobs, uready = len(self._unpack_jobs), len(self._unpack_ready)
Brian Harring0be85c62012-03-17 19:52:12 -07001551 bjobs, bready = len(self._build_jobs), len(self._build_ready)
1552 retries = len(self._retry_queue)
1553 pending = max(0, len(self._deps_map) - fjobs - bjobs)
1554 line = "Pending %s/%s, " % (pending, self._total_jobs)
1555 if fjobs or fready:
1556 line += "Fetching %s/%s, " % (fjobs, fready + fjobs)
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001557 if ujobs or uready:
1558 line += "Unpacking %s/%s, " % (ujobs, uready + ujobs)
Brian Harring0be85c62012-03-17 19:52:12 -07001559 if bjobs or bready or retries:
1560 line += "Building %s/%s, " % (bjobs, bready + bjobs)
1561 if retries:
1562 line += "Retrying %s, " % (retries,)
Mike Frysingerd6e2df02014-11-26 02:55:04 -05001563 load = " ".join(str(x) for x in os.getloadavg())
1564 line += ("[Time %dm%.1fs Load %s]" % (seconds / 60, seconds % 60, load))
Brian Harring0be85c62012-03-17 19:52:12 -07001565 self._Print(line)
David Jamesfcb70ef2011-02-02 16:02:30 -08001566
1567 def _Finish(self, target):
David James8c7e5e32011-06-28 11:26:03 -07001568 """Mark a target as completed and unblock dependencies."""
1569 this_pkg = self._deps_map[target]
1570 if this_pkg["needs"] and this_pkg["nodeps"]:
1571 # We got installed, but our deps have not been installed yet. Dependent
1572 # packages should only be installed when our needs have been fully met.
1573 this_pkg["action"] = "nomerge"
1574 else:
David James8c7e5e32011-06-28 11:26:03 -07001575 for dep in this_pkg["provides"]:
1576 dep_pkg = self._deps_map[dep]
Brian Harring0be85c62012-03-17 19:52:12 -07001577 state = self._state_map[dep]
David James8c7e5e32011-06-28 11:26:03 -07001578 del dep_pkg["needs"][target]
Brian Harring0be85c62012-03-17 19:52:12 -07001579 state.update_score()
1580 if not state.prefetched:
1581 if dep in self._fetch_ready:
1582 # If it's not currently being fetched, update the prioritization
1583 self._fetch_ready.sort()
1584 elif not dep_pkg["needs"]:
David James8c7e5e32011-06-28 11:26:03 -07001585 if dep_pkg["nodeps"] and dep_pkg["action"] == "nomerge":
1586 self._Finish(dep)
1587 else:
Brian Harring0be85c62012-03-17 19:52:12 -07001588 self._build_ready.put(self._state_map[dep])
David James8c7e5e32011-06-28 11:26:03 -07001589 self._deps_map.pop(target)
David Jamesfcb70ef2011-02-02 16:02:30 -08001590
1591 def _Retry(self):
David James8c7e5e32011-06-28 11:26:03 -07001592 while self._retry_queue:
Brian Harring0be85c62012-03-17 19:52:12 -07001593 state = self._retry_queue.pop(0)
1594 if self._Schedule(state):
1595 self._Print("Retrying emerge of %s." % state.target)
David James8c7e5e32011-06-28 11:26:03 -07001596 break
David Jamesfcb70ef2011-02-02 16:02:30 -08001597
Brian Harringa43f5952012-04-12 01:19:34 -07001598 def _Shutdown(self):
David Jamesfcb70ef2011-02-02 16:02:30 -08001599 # Tell emerge workers to exit. They all exit when 'None' is pushed
1600 # to the queue.
Brian Harring0be85c62012-03-17 19:52:12 -07001601
Brian Harringa43f5952012-04-12 01:19:34 -07001602 # Shutdown the workers first; then jobs (which is how they feed things back)
1603 # then finally the print queue.
Brian Harring0be85c62012-03-17 19:52:12 -07001604
Brian Harringa43f5952012-04-12 01:19:34 -07001605 def _stop(queue, pool):
1606 if pool is None:
1607 return
1608 try:
1609 queue.put(None)
1610 pool.close()
1611 pool.join()
1612 finally:
1613 pool.terminate()
Brian Harring0be85c62012-03-17 19:52:12 -07001614
Brian Harringa43f5952012-04-12 01:19:34 -07001615 _stop(self._fetch_queue, self._fetch_pool)
1616 self._fetch_queue = self._fetch_pool = None
Brian Harring0be85c62012-03-17 19:52:12 -07001617
Brian Harringa43f5952012-04-12 01:19:34 -07001618 _stop(self._build_queue, self._build_pool)
1619 self._build_queue = self._build_pool = None
1620
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001621 if self._unpack_only:
1622 _stop(self._unpack_queue, self._unpack_pool)
1623 self._unpack_queue = self._unpack_pool = None
1624
Brian Harringa43f5952012-04-12 01:19:34 -07001625 if self._job_queue is not None:
1626 self._job_queue.close()
1627 self._job_queue = None
David Jamesfcb70ef2011-02-02 16:02:30 -08001628
1629 # Now that our workers are finished, we can kill the print queue.
Brian Harringa43f5952012-04-12 01:19:34 -07001630 if self._print_worker is not None:
1631 try:
1632 self._print_queue.put(None)
1633 self._print_queue.close()
1634 self._print_worker.join()
1635 finally:
1636 self._print_worker.terminate()
1637 self._print_queue = self._print_worker = None
David Jamesfcb70ef2011-02-02 16:02:30 -08001638
1639 def Run(self):
1640 """Run through the scheduled ebuilds.
1641
1642 Keep running so long as we have uninstalled packages in the
1643 dependency graph to merge.
1644 """
Brian Harringa43f5952012-04-12 01:19:34 -07001645 if not self._deps_map:
1646 return
1647
Brian Harring0be85c62012-03-17 19:52:12 -07001648 # Start the fetchers.
1649 for _ in xrange(min(self._fetch_procs, len(self._fetch_ready))):
1650 state = self._fetch_ready.get()
1651 self._fetch_jobs[state.target] = None
1652 self._fetch_queue.put(state)
1653
1654 # Print an update, then get going.
1655 self._Status()
1656
David Jamesfcb70ef2011-02-02 16:02:30 -08001657 while self._deps_map:
1658 # Check here that we are actually waiting for something.
Brian Harring0be85c62012-03-17 19:52:12 -07001659 if (self._build_queue.empty() and
David Jamesfcb70ef2011-02-02 16:02:30 -08001660 self._job_queue.empty() and
Brian Harring0be85c62012-03-17 19:52:12 -07001661 not self._fetch_jobs and
1662 not self._fetch_ready and
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001663 not self._unpack_jobs and
1664 not self._unpack_ready and
Brian Harring0be85c62012-03-17 19:52:12 -07001665 not self._build_jobs and
1666 not self._build_ready and
David Jamesfcb70ef2011-02-02 16:02:30 -08001667 self._deps_map):
1668 # If we have failed on a package, retry it now.
1669 if self._retry_queue:
1670 self._Retry()
1671 else:
David Jamesfcb70ef2011-02-02 16:02:30 -08001672 # Tell the user why we're exiting.
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001673 if self._failed_count:
1674 print('Packages failed:\n\t%s' %
1675 '\n\t'.join(self._failed_count.iterkeys()))
David James0eae23e2012-07-03 15:04:25 -07001676 status_file = os.environ.get("PARALLEL_EMERGE_STATUS_FILE")
1677 if status_file:
David James321490a2012-12-17 12:05:56 -08001678 failed_pkgs = set(portage.versions.cpv_getkey(x)
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001679 for x in self._failed_count.iterkeys())
David James0eae23e2012-07-03 15:04:25 -07001680 with open(status_file, "a") as f:
1681 f.write("%s\n" % " ".join(failed_pkgs))
David Jamesfcb70ef2011-02-02 16:02:30 -08001682 else:
Mike Frysinger383367e2014-09-16 15:06:17 -04001683 print("Deadlock! Circular dependencies!")
David Jamesfcb70ef2011-02-02 16:02:30 -08001684 sys.exit(1)
1685
David James321490a2012-12-17 12:05:56 -08001686 for _ in xrange(12):
David Jamesa74289a2011-08-12 10:41:24 -07001687 try:
1688 job = self._job_queue.get(timeout=5)
1689 break
1690 except Queue.Empty:
1691 # Check if any more jobs can be scheduled.
1692 self._ScheduleLoop()
1693 else:
Brian Harring706747c2012-03-16 03:04:31 -07001694 # Print an update every 60 seconds.
David Jamesfcb70ef2011-02-02 16:02:30 -08001695 self._Status()
1696 continue
1697
1698 target = job.target
1699
Brian Harring0be85c62012-03-17 19:52:12 -07001700 if job.fetch_only:
1701 if not job.done:
1702 self._fetch_jobs[job.target] = job
1703 else:
1704 state = self._state_map[job.target]
1705 state.prefetched = True
1706 state.fetched_successfully = (job.retcode == 0)
1707 del self._fetch_jobs[job.target]
1708 self._Print("Fetched %s in %2.2fs"
1709 % (target, time.time() - job.start_timestamp))
1710
1711 if self._show_output or job.retcode != 0:
1712 self._print_queue.put(JobPrinter(job, unlink=True))
1713 else:
1714 os.unlink(job.filename)
1715 # Failure or not, let build work with it next.
1716 if not self._deps_map[job.target]["needs"]:
1717 self._build_ready.put(state)
1718 self._ScheduleLoop()
1719
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001720 if self._unpack_only and job.retcode == 0:
1721 self._unpack_ready.put(state)
1722 self._ScheduleLoop(unpack_only=True)
1723
Brian Harring0be85c62012-03-17 19:52:12 -07001724 if self._fetch_ready:
1725 state = self._fetch_ready.get()
1726 self._fetch_queue.put(state)
1727 self._fetch_jobs[state.target] = None
1728 else:
1729 # Minor optimization; shut down fetchers early since we know
1730 # the queue is empty.
1731 self._fetch_queue.put(None)
1732 continue
1733
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001734 if job.unpack_only:
1735 if not job.done:
1736 self._unpack_jobs[target] = job
1737 else:
1738 del self._unpack_jobs[target]
1739 self._Print("Unpacked %s in %2.2fs"
1740 % (target, time.time() - job.start_timestamp))
1741 if self._show_output or job.retcode != 0:
1742 self._print_queue.put(JobPrinter(job, unlink=True))
1743 else:
1744 os.unlink(job.filename)
1745 if self._unpack_ready:
1746 state = self._unpack_ready.get()
1747 self._unpack_queue.put(state)
1748 self._unpack_jobs[state.target] = None
1749 continue
1750
David Jamesfcb70ef2011-02-02 16:02:30 -08001751 if not job.done:
Brian Harring0be85c62012-03-17 19:52:12 -07001752 self._build_jobs[target] = job
David Jamesfcb70ef2011-02-02 16:02:30 -08001753 self._Print("Started %s (logged in %s)" % (target, job.filename))
1754 continue
1755
1756 # Print output of job
1757 if self._show_output or job.retcode != 0:
1758 self._print_queue.put(JobPrinter(job, unlink=True))
1759 else:
1760 os.unlink(job.filename)
Brian Harring0be85c62012-03-17 19:52:12 -07001761 del self._build_jobs[target]
David Jamesfcb70ef2011-02-02 16:02:30 -08001762
1763 seconds = time.time() - job.start_timestamp
1764 details = "%s (in %dm%.1fs)" % (target, seconds / 60, seconds % 60)
1765
1766 # Complain if necessary.
1767 if job.retcode != 0:
1768 # Handle job failure.
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001769 failed_count = self._failed_count.get(target, 0)
1770 if failed_count >= self._max_retries:
1771 # If this job has failed and can't be retried, give up.
David Jamesfcb70ef2011-02-02 16:02:30 -08001772 self._Print("Failed %s. Your build has failed." % details)
1773 else:
1774 # Queue up this build to try again after a long while.
Brian Harring0be85c62012-03-17 19:52:12 -07001775 self._retry_queue.append(self._state_map[target])
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001776 self._failed_count[target] = failed_count + 1
David Jamesfcb70ef2011-02-02 16:02:30 -08001777 self._Print("Failed %s, retrying later." % details)
1778 else:
David James32420cc2011-08-25 21:32:46 -07001779 self._Print("Completed %s" % details)
1780
1781 # Mark as completed and unblock waiting ebuilds.
1782 self._Finish(target)
1783
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001784 if target in self._failed_count and self._retry_queue:
David Jamesfcb70ef2011-02-02 16:02:30 -08001785 # If we have successfully retried a failed package, and there
1786 # are more failed packages, try the next one. We will only have
1787 # one retrying package actively running at a time.
1788 self._Retry()
1789
David Jamesfcb70ef2011-02-02 16:02:30 -08001790
David James8c7e5e32011-06-28 11:26:03 -07001791 # Schedule pending jobs and print an update.
1792 self._ScheduleLoop()
1793 self._Status()
David Jamesfcb70ef2011-02-02 16:02:30 -08001794
David Jamese703d0f2012-01-12 16:27:45 -08001795 # If packages were retried, output a warning.
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001796 if self._failed_count:
David Jamese703d0f2012-01-12 16:27:45 -08001797 self._Print("")
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001798 self._Print("WARNING: The following packages failed once or more,")
David Jamese703d0f2012-01-12 16:27:45 -08001799 self._Print("but succeeded upon retry. This might indicate incorrect")
1800 self._Print("dependencies.")
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001801 for pkg in self._failed_count.iterkeys():
David Jamese703d0f2012-01-12 16:27:45 -08001802 self._Print(" %s" % pkg)
1803 self._Print("@@@STEP_WARNINGS@@@")
1804 self._Print("")
1805
David Jamesfcb70ef2011-02-02 16:02:30 -08001806 # Tell child threads to exit.
1807 self._Print("Merge complete")
David Jamesfcb70ef2011-02-02 16:02:30 -08001808
1809
Brian Harring30675052012-02-29 12:18:22 -08001810def main(argv):
Brian Harring8294d652012-05-23 02:20:52 -07001811 try:
1812 return real_main(argv)
1813 finally:
1814 # Work around multiprocessing sucking and not cleaning up after itself.
1815 # http://bugs.python.org/issue4106;
1816 # Step one; ensure GC is ran *prior* to the VM starting shutdown.
1817 gc.collect()
1818 # Step two; go looking for those threads and try to manually reap
1819 # them if we can.
1820 for x in threading.enumerate():
1821 # Filter on the name, and ident; if ident is None, the thread
1822 # wasn't started.
1823 if x.name == 'QueueFeederThread' and x.ident is not None:
1824 x.join(1)
David Jamesfcb70ef2011-02-02 16:02:30 -08001825
Brian Harring8294d652012-05-23 02:20:52 -07001826
1827def real_main(argv):
Brian Harring30675052012-02-29 12:18:22 -08001828 parallel_emerge_args = argv[:]
David Jamesfcb70ef2011-02-02 16:02:30 -08001829 deps = DepGraphGenerator()
Brian Harring30675052012-02-29 12:18:22 -08001830 deps.Initialize(parallel_emerge_args)
David Jamesfcb70ef2011-02-02 16:02:30 -08001831 emerge = deps.emerge
1832
1833 if emerge.action is not None:
Brian Harring30675052012-02-29 12:18:22 -08001834 argv = deps.ParseParallelEmergeArgs(argv)
Brian Harring8294d652012-05-23 02:20:52 -07001835 return emerge_main(argv)
David Jamesfcb70ef2011-02-02 16:02:30 -08001836 elif not emerge.cmdline_packages:
1837 Usage()
Brian Harring8294d652012-05-23 02:20:52 -07001838 return 1
David Jamesfcb70ef2011-02-02 16:02:30 -08001839
1840 # Unless we're in pretend mode, there's not much point running without
1841 # root access. We need to be able to install packages.
1842 #
1843 # NOTE: Even if you're running --pretend, it's a good idea to run
1844 # parallel_emerge with root access so that portage can write to the
1845 # dependency cache. This is important for performance.
David James321490a2012-12-17 12:05:56 -08001846 if "--pretend" not in emerge.opts and portage.data.secpass < 2:
Mike Frysinger383367e2014-09-16 15:06:17 -04001847 print("parallel_emerge: superuser access is required.")
Brian Harring8294d652012-05-23 02:20:52 -07001848 return 1
David Jamesfcb70ef2011-02-02 16:02:30 -08001849
1850 if "--quiet" not in emerge.opts:
1851 cmdline_packages = " ".join(emerge.cmdline_packages)
Mike Frysinger383367e2014-09-16 15:06:17 -04001852 print("Starting fast-emerge.")
1853 print(" Building package %s on %s" % (cmdline_packages,
1854 deps.board or "root"))
David Jamesfcb70ef2011-02-02 16:02:30 -08001855
David James386ccd12011-05-04 20:17:42 -07001856 deps_tree, deps_info = deps.GenDependencyTree()
David Jamesfcb70ef2011-02-02 16:02:30 -08001857
1858 # You want me to be verbose? I'll give you two trees! Twice as much value.
1859 if "--tree" in emerge.opts and "--verbose" in emerge.opts:
1860 deps.PrintTree(deps_tree)
1861
David James386ccd12011-05-04 20:17:42 -07001862 deps_graph = deps.GenDependencyGraph(deps_tree, deps_info)
David Jamesfcb70ef2011-02-02 16:02:30 -08001863
1864 # OK, time to print out our progress so far.
1865 deps.PrintInstallPlan(deps_graph)
1866 if "--tree" in emerge.opts:
1867 PrintDepsMap(deps_graph)
1868
1869 # Are we upgrading portage? If so, and there are more packages to merge,
1870 # schedule a restart of parallel_emerge to merge the rest. This ensures that
1871 # we pick up all updates to portage settings before merging any more
1872 # packages.
1873 portage_upgrade = False
1874 root = emerge.settings["ROOT"]
Don Garrett25f309a2014-03-19 14:02:12 -07001875 # pylint: disable=W0212
Bertrand SIMONNETa15b5072014-10-23 15:27:52 -07001876 final_db = emerge.depgraph._dynamic_config._filtered_trees[root]['graph_db']
David Jamesfcb70ef2011-02-02 16:02:30 -08001877 if root == "/":
1878 for db_pkg in final_db.match_pkgs("sys-apps/portage"):
1879 portage_pkg = deps_graph.get(db_pkg.cpv)
David James0ff16f22012-11-02 14:18:07 -07001880 if portage_pkg:
David Jamesfcb70ef2011-02-02 16:02:30 -08001881 portage_upgrade = True
1882 if "--quiet" not in emerge.opts:
Mike Frysinger383367e2014-09-16 15:06:17 -04001883 print("Upgrading portage first, then restarting...")
David Jamesfcb70ef2011-02-02 16:02:30 -08001884
David James0ff16f22012-11-02 14:18:07 -07001885 # Upgrade Portage first, then the rest of the packages.
1886 #
1887 # In order to grant the child permission to run setsid, we need to run sudo
1888 # again. We preserve SUDO_USER here in case an ebuild depends on it.
1889 if portage_upgrade:
1890 # Calculate what arguments to use when re-invoking.
1891 args = ["sudo", "-E", "SUDO_USER=%s" % os.environ.get("SUDO_USER", "")]
1892 args += [os.path.abspath(sys.argv[0])] + parallel_emerge_args
1893 args += ["--exclude=sys-apps/portage"]
1894
1895 # First upgrade Portage.
1896 passthrough_args = ("--quiet", "--pretend", "--verbose")
1897 emerge_args = [k for k in emerge.opts if k in passthrough_args]
1898 ret = emerge_main(emerge_args + ["portage"])
1899 if ret != 0:
1900 return ret
1901
1902 # Now upgrade the rest.
1903 os.execvp(args[0], args)
1904
Bertrand SIMONNETc03c8ee2014-12-10 17:02:55 -08001905 # Attempt to solve crbug.com/433482
1906 # The file descriptor error appears only when getting userpriv_groups
1907 # (lazily generated). Loading userpriv_groups here will reduce the number of
1908 # calls from few hundreds to one.
1909 portage.data._get_global('userpriv_groups')
1910
David Jamesfcb70ef2011-02-02 16:02:30 -08001911 # Run the queued emerges.
Thiago Goncalesf4acc422013-07-17 10:26:35 -07001912 scheduler = EmergeQueue(deps_graph, emerge, deps.package_db, deps.show_output,
Bertrand SIMONNET411945d2015-05-20 17:23:28 -07001913 deps.unpack_only, deps.max_retries)
Brian Harringa43f5952012-04-12 01:19:34 -07001914 try:
1915 scheduler.Run()
1916 finally:
Don Garrett25f309a2014-03-19 14:02:12 -07001917 # pylint: disable=W0212
Brian Harringa43f5952012-04-12 01:19:34 -07001918 scheduler._Shutdown()
David James97ce8902011-08-16 09:51:05 -07001919 scheduler = None
David Jamesfcb70ef2011-02-02 16:02:30 -08001920
Mike Frysingerd20a6e22012-10-04 19:01:10 -04001921 clean_logs(emerge.settings)
1922
Mike Frysinger383367e2014-09-16 15:06:17 -04001923 print("Done")
Brian Harring8294d652012-05-23 02:20:52 -07001924 return 0