mirror of
https://github.com/flatcar/scripts.git
synced 2025-09-26 08:01:14 +02:00
Update parallel_emerge to support --workon.
Packages specified as workon packages are always built from source. Dependencies of workon packages are also built from source. Dependencies are calculated with --selective=n so that workon packages will be included in the install list. Packages that are not being worked on and are being unnecessarily replaced because of --selective=n are filtered out later. This patch also fixes a bug with world file updating -- previously the world file was never updated because the packages were getting removed by RemoveInstalledPackages. We now keep the appropriate world file updates, and instead update SanitizeTree to handle world file updates without trouble. I also optimized the cycle cracking algorithm so that it only visits each node once. This seems to improve run time significantly on graphs that have cycles. TEST=Ran ./parallel_emerge -uDNvpg --board=x86-generic chromeos --workon=chromeos-chrome && ./parallel_emerge -uDNvpg --board=x86-generic chromeos --workon=libcros && ./parallel_emerge -uDNvpg world --workon=libxml2 && ./parallel_emerge -uDNv -p world hard-host-depends --workon='bcel' BUG=none Review URL: http://codereview.chromium.org/2959006
This commit is contained in:
parent
42ca818d3b
commit
b9ad46e9f7
325
parallel_emerge
325
parallel_emerge
@ -6,7 +6,8 @@
|
|||||||
"""Program to run emerge in parallel, for significant speedup.
|
"""Program to run emerge in parallel, for significant speedup.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
./parallel_emerge --board=BOARD [emerge args] package
|
./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps]
|
||||||
|
[emerge args] package"
|
||||||
|
|
||||||
Basic operation:
|
Basic operation:
|
||||||
Runs 'emerge -p --debug' to display dependencies, and stores a
|
Runs 'emerge -p --debug' to display dependencies, and stores a
|
||||||
@ -44,11 +45,25 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
import _emerge.main
|
||||||
|
|
||||||
|
|
||||||
def Usage():
|
def Usage():
|
||||||
|
"""Print usage."""
|
||||||
print "Usage:"
|
print "Usage:"
|
||||||
print " ./parallel_emerge --board=BOARD --jobs=JOBS [emerge args] package"
|
print " ./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps]"
|
||||||
|
print " [emerge args] package"
|
||||||
|
print
|
||||||
|
print "Packages specified as workon packages are always built from source."
|
||||||
|
print "Unless --no-workon-deps is specified, packages that depend on these"
|
||||||
|
print "packages are also built from source."
|
||||||
|
print
|
||||||
|
print "The --workon argument is mainly useful when you want to build and"
|
||||||
|
print "install packages that you are working on unconditionally, but do not"
|
||||||
|
print "to have to rev the package to indicate you want to build it from"
|
||||||
|
print "source. The build_packages script will automatically supply the"
|
||||||
|
print "workon argument to emerge, ensuring that packages selected using"
|
||||||
|
print "cros-workon are rebuilt."
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
@ -56,12 +71,6 @@ def Usage():
|
|||||||
# but will prevent the package from installing.
|
# but will prevent the package from installing.
|
||||||
secret_deps = {}
|
secret_deps = {}
|
||||||
|
|
||||||
# Globals: package we are building, board we are targeting,
|
|
||||||
# emerge args we are passing through.
|
|
||||||
PACKAGE = None
|
|
||||||
EMERGE_ARGS = ""
|
|
||||||
BOARD = None
|
|
||||||
|
|
||||||
# Runtime flags. TODO(): Maybe make these command-line options or
|
# Runtime flags. TODO(): Maybe make these command-line options or
|
||||||
# environment variables.
|
# environment variables.
|
||||||
VERBOSE = False
|
VERBOSE = False
|
||||||
@ -75,12 +84,8 @@ def ParseArgs(argv):
|
|||||||
"""Set global vars based on command line.
|
"""Set global vars based on command line.
|
||||||
|
|
||||||
We need to be compatible with emerge arg format.
|
We need to be compatible with emerge arg format.
|
||||||
We scrape --board=XXX and --jobs=XXX, and distinguish between args
|
We scrape arguments that are specific to parallel_emerge, and pass through
|
||||||
and package names.
|
the rest directly to emerge.
|
||||||
TODO(): Robustify argument processing, as it's possible to
|
|
||||||
pass in many two argument parameters that are difficult
|
|
||||||
to programmatically identify, although we don't currently
|
|
||||||
use any besides --with-bdeps <y|n>.
|
|
||||||
Args:
|
Args:
|
||||||
argv: arguments list
|
argv: arguments list
|
||||||
Returns:
|
Returns:
|
||||||
@ -88,37 +93,28 @@ def ParseArgs(argv):
|
|||||||
"""
|
"""
|
||||||
if VERBOSE:
|
if VERBOSE:
|
||||||
print argv
|
print argv
|
||||||
board_arg = None
|
workon_set = set()
|
||||||
jobs_arg = 0
|
myopts = {}
|
||||||
package_args = []
|
myopts["workon"] = workon_set
|
||||||
emerge_passthru_args = ""
|
emerge_args = []
|
||||||
for arg in argv[1:]:
|
for arg in argv[1:]:
|
||||||
# Specifically match "--board=" and "--jobs=".
|
# Specifically match arguments that are specific to parallel_emerge, and
|
||||||
|
# pass through the rest.
|
||||||
if arg.startswith("--board="):
|
if arg.startswith("--board="):
|
||||||
board_arg = arg.replace("--board=", "")
|
myopts["board"] = arg.replace("--board=", "")
|
||||||
elif arg.startswith("--jobs="):
|
elif arg.startswith("--workon="):
|
||||||
try:
|
workon_str = arg.replace("--workon=", "")
|
||||||
jobs_arg = int(arg.replace("--jobs=", ""))
|
workon_set.update(shlex.split(" ".join(shlex.split(workon_str))))
|
||||||
except ValueError:
|
elif arg == "--no-workon-deps":
|
||||||
print "Unrecognized argument:", arg
|
myopts["no-workon-deps"] = True
|
||||||
Usage()
|
|
||||||
sys.exit(1)
|
|
||||||
elif arg.startswith("-") or arg == "y" or arg == "n":
|
|
||||||
# Not a package name, so pass through to emerge.
|
|
||||||
emerge_passthru_args = emerge_passthru_args + " " + arg
|
|
||||||
else:
|
else:
|
||||||
package_args.append(arg)
|
# Not a package name, so pass through to emerge.
|
||||||
|
emerge_args.append(arg)
|
||||||
|
|
||||||
if not package_args and not emerge_passthru_args:
|
emerge_action, emerge_opts, emerge_files = _emerge.main.parse_opts(
|
||||||
Usage()
|
emerge_args)
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# Default to lots of jobs
|
return myopts, emerge_action, emerge_opts, emerge_files
|
||||||
if jobs_arg <= 0:
|
|
||||||
jobs_arg = 256
|
|
||||||
|
|
||||||
# Set globals.
|
|
||||||
return " ".join(package_args), emerge_passthru_args, board_arg, jobs_arg
|
|
||||||
|
|
||||||
|
|
||||||
def EmergeCommand():
|
def EmergeCommand():
|
||||||
@ -130,9 +126,15 @@ def EmergeCommand():
|
|||||||
string containing emerge command.
|
string containing emerge command.
|
||||||
"""
|
"""
|
||||||
emerge = "emerge"
|
emerge = "emerge"
|
||||||
if BOARD:
|
if "board" in OPTS:
|
||||||
emerge += "-" + BOARD
|
emerge += "-" + OPTS["board"]
|
||||||
return emerge + " " + EMERGE_ARGS
|
cmd = [emerge]
|
||||||
|
for key, val in EMERGE_OPTS.items():
|
||||||
|
if val is True:
|
||||||
|
cmd.append(key)
|
||||||
|
else:
|
||||||
|
cmd.extend([key, str(val)])
|
||||||
|
return " ".join(cmd)
|
||||||
|
|
||||||
|
|
||||||
def GetDepsFromPortage(package):
|
def GetDepsFromPortage(package):
|
||||||
@ -147,7 +149,10 @@ def GetDepsFromPortage(package):
|
|||||||
Text output of emerge -p --debug, which can be processed elsewhere.
|
Text output of emerge -p --debug, which can be processed elsewhere.
|
||||||
"""
|
"""
|
||||||
print "Calculating deps for package %s" % package
|
print "Calculating deps for package %s" % package
|
||||||
cmdline = EmergeCommand() + " -p --debug --color=n " + package
|
cmdline = (EmergeCommand() + " -p --debug --color=n --with-bdeps=y " +
|
||||||
|
"--selective=n " + package)
|
||||||
|
if OPTS["workon"]:
|
||||||
|
cmdline += " " + " ".join(OPTS["workon"])
|
||||||
print "+ %s" % cmdline
|
print "+ %s" % cmdline
|
||||||
|
|
||||||
# Store output in a temp file as it is too big for a unix pipe.
|
# Store output in a temp file as it is too big for a unix pipe.
|
||||||
@ -155,11 +160,11 @@ def GetDepsFromPortage(package):
|
|||||||
stdout_buffer = tempfile.TemporaryFile()
|
stdout_buffer = tempfile.TemporaryFile()
|
||||||
# Launch the subprocess.
|
# Launch the subprocess.
|
||||||
start = time.time()
|
start = time.time()
|
||||||
depsproc = subprocess.Popen(shlex.split(cmdline), stderr=stderr_buffer,
|
depsproc = subprocess.Popen(shlex.split(str(cmdline)), stderr=stderr_buffer,
|
||||||
stdout=stdout_buffer, bufsize=64*1024)
|
stdout=stdout_buffer, bufsize=64*1024)
|
||||||
depsproc.wait()
|
depsproc.wait()
|
||||||
seconds = time.time() - start
|
seconds = time.time() - start
|
||||||
print "Deps calculated in %d:%04.1fs" % (seconds / 60, seconds % 60)
|
print "Deps calculated in %dm%.1fs" % (seconds / 60, seconds % 60)
|
||||||
stderr_buffer.seek(0)
|
stderr_buffer.seek(0)
|
||||||
stderr_raw = stderr_buffer.read()
|
stderr_raw = stderr_buffer.read()
|
||||||
info_start = stderr_raw.find("digraph")
|
info_start = stderr_raw.find("digraph")
|
||||||
@ -259,6 +264,10 @@ def DepsToTree(lines):
|
|||||||
updatedep[fullpkg].setdefault("action", doins)
|
updatedep[fullpkg].setdefault("action", doins)
|
||||||
# Add the type of dep.
|
# Add the type of dep.
|
||||||
updatedep[fullpkg].setdefault("deptype", deptype)
|
updatedep[fullpkg].setdefault("deptype", deptype)
|
||||||
|
# Add the long name of the package
|
||||||
|
updatedep[fullpkg].setdefault("pkgpath", "%s/%s" % (pkgdir, pkgname))
|
||||||
|
# Add the short name of the package
|
||||||
|
updatedep[fullpkg].setdefault("pkgname", pkgname)
|
||||||
|
|
||||||
# Drop any stack entries below our depth.
|
# Drop any stack entries below our depth.
|
||||||
deps_stack = deps_stack[0:depth]
|
deps_stack = deps_stack[0:depth]
|
||||||
@ -283,6 +292,8 @@ def DepsToTree(lines):
|
|||||||
# Add the type of dep.
|
# Add the type of dep.
|
||||||
updatedep[pkgname].setdefault("action", "world")
|
updatedep[pkgname].setdefault("action", "world")
|
||||||
updatedep[pkgname].setdefault("deptype", "normal")
|
updatedep[pkgname].setdefault("deptype", "normal")
|
||||||
|
updatedep[pkgname].setdefault("pkgpath", None)
|
||||||
|
updatedep[pkgname].setdefault("pkgname", None)
|
||||||
|
|
||||||
# Drop any obsolete stack entries.
|
# Drop any obsolete stack entries.
|
||||||
deps_stack = deps_stack[0:depth]
|
deps_stack = deps_stack[0:depth]
|
||||||
@ -297,12 +308,14 @@ def DepsToTree(lines):
|
|||||||
uninstall = False
|
uninstall = False
|
||||||
if oldversion and (desc.find("U") != -1 or desc.find("D") != -1):
|
if oldversion and (desc.find("U") != -1 or desc.find("D") != -1):
|
||||||
uninstall = True
|
uninstall = True
|
||||||
|
replace = desc.find("R") != -1
|
||||||
fullpkg = "%s/%s-%s" % (pkgdir, pkgname, version)
|
fullpkg = "%s/%s-%s" % (pkgdir, pkgname, version)
|
||||||
deps_info[fullpkg] = {"idx": len(deps_info),
|
deps_info[fullpkg] = {"idx": len(deps_info),
|
||||||
"pkgdir": pkgdir,
|
"pkgdir": pkgdir,
|
||||||
"pkgname": pkgname,
|
"pkgname": pkgname,
|
||||||
"oldversion": oldversion,
|
"oldversion": oldversion,
|
||||||
"uninstall": uninstall}
|
"uninstall": uninstall,
|
||||||
|
"replace": replace}
|
||||||
else:
|
else:
|
||||||
# Is this a package that failed to match our huge regex?
|
# Is this a package that failed to match our huge regex?
|
||||||
m = re_failed.match(line)
|
m = re_failed.match(line)
|
||||||
@ -328,17 +341,19 @@ def PrintTree(deps, depth=""):
|
|||||||
PrintTree(deps[entry]["deps"], depth=depth + " ")
|
PrintTree(deps[entry]["deps"], depth=depth + " ")
|
||||||
|
|
||||||
|
|
||||||
def GenDependencyGraph(deps_tree, deps_info):
|
def GenDependencyGraph(deps_tree, deps_info, package_names):
|
||||||
"""Generate a doubly linked dependency graph.
|
"""Generate a doubly linked dependency graph.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
deps_tree: Dependency tree structure.
|
deps_tree: Dependency tree structure.
|
||||||
deps_info: More details on the dependencies.
|
deps_info: More details on the dependencies.
|
||||||
|
package_names: Names of packages to add to the world file.
|
||||||
Returns:
|
Returns:
|
||||||
Deps graph in the form of a dict of packages, with each package
|
Deps graph in the form of a dict of packages, with each package
|
||||||
specifying a "needs" list and "provides" list.
|
specifying a "needs" list and "provides" list.
|
||||||
"""
|
"""
|
||||||
deps_map = {}
|
deps_map = {}
|
||||||
|
pkgpaths = {}
|
||||||
|
|
||||||
def ReverseTree(packages):
|
def ReverseTree(packages):
|
||||||
"""Convert tree to digraph.
|
"""Convert tree to digraph.
|
||||||
@ -352,8 +367,13 @@ def GenDependencyGraph(deps_tree, deps_info):
|
|||||||
"""
|
"""
|
||||||
for pkg in packages:
|
for pkg in packages:
|
||||||
action = packages[pkg]["action"]
|
action = packages[pkg]["action"]
|
||||||
|
pkgpath = packages[pkg]["pkgpath"]
|
||||||
|
pkgname = packages[pkg]["pkgname"]
|
||||||
|
pkgpaths[pkgpath] = pkg
|
||||||
|
pkgpaths[pkgname] = pkg
|
||||||
this_pkg = deps_map.setdefault(
|
this_pkg = deps_map.setdefault(
|
||||||
pkg, {"needs": set(), "provides": set(), "action": "nomerge"})
|
pkg, {"needs": {}, "provides": set(), "action": "nomerge",
|
||||||
|
"workon": False, "cmdline": False})
|
||||||
if action != "nomerge":
|
if action != "nomerge":
|
||||||
this_pkg["action"] = action
|
this_pkg["action"] = action
|
||||||
this_pkg["deps_info"] = deps_info.get(pkg)
|
this_pkg["deps_info"] = deps_info.get(pkg)
|
||||||
@ -363,14 +383,25 @@ def GenDependencyGraph(deps_tree, deps_info):
|
|||||||
dep_type = dep_item["deptype"]
|
dep_type = dep_item["deptype"]
|
||||||
if dep_type != "(runtime_post)":
|
if dep_type != "(runtime_post)":
|
||||||
dep_pkg["provides"].add(pkg)
|
dep_pkg["provides"].add(pkg)
|
||||||
this_pkg["needs"].add(dep)
|
this_pkg["needs"][dep] = dep_type
|
||||||
|
|
||||||
def RemoveInstalledPackages():
|
def RemoveInstalledPackages():
|
||||||
"""Remove installed packages, propagating dependencies."""
|
"""Remove installed packages, propagating dependencies."""
|
||||||
|
|
||||||
|
if "--selective" in EMERGE_OPTS:
|
||||||
|
selective = EMERGE_OPTS["--selective"] != "n"
|
||||||
|
else:
|
||||||
|
selective = "--noreplace" in EMERGE_OPTS or "--update" in EMERGE_OPTS
|
||||||
rm_pkgs = set(deps_map.keys()) - set(deps_info.keys())
|
rm_pkgs = set(deps_map.keys()) - set(deps_info.keys())
|
||||||
|
for pkg, info in deps_info.items():
|
||||||
|
if selective and not deps_map[pkg]["workon"] and info["replace"]:
|
||||||
|
rm_pkgs.add(pkg)
|
||||||
for pkg in rm_pkgs:
|
for pkg in rm_pkgs:
|
||||||
this_pkg = deps_map[pkg]
|
this_pkg = deps_map[pkg]
|
||||||
|
if this_pkg["cmdline"] and "--oneshot" not in EMERGE_OPTS:
|
||||||
|
# If "cmdline" is set, this is a world update that was passed on the
|
||||||
|
# command-line. Keep these unless we're in --oneshot mode.
|
||||||
|
continue
|
||||||
needs = this_pkg["needs"]
|
needs = this_pkg["needs"]
|
||||||
provides = this_pkg["provides"]
|
provides = this_pkg["provides"]
|
||||||
for dep in needs:
|
for dep in needs:
|
||||||
@ -381,47 +412,52 @@ def GenDependencyGraph(deps_tree, deps_info):
|
|||||||
for target in provides:
|
for target in provides:
|
||||||
target_needs = deps_map[target]["needs"]
|
target_needs = deps_map[target]["needs"]
|
||||||
target_needs.update(needs)
|
target_needs.update(needs)
|
||||||
target_needs.discard(pkg)
|
if pkg in target_needs:
|
||||||
target_needs.discard(target)
|
del target_needs[pkg]
|
||||||
|
if target in target_needs:
|
||||||
|
del target_needs[target]
|
||||||
del deps_map[pkg]
|
del deps_map[pkg]
|
||||||
|
|
||||||
def SanitizeDep(basedep, currdep, oldstack, limit):
|
def SanitizeDep(basedep, currdep, visited, cycle):
|
||||||
"""Search for circular deps between basedep and currdep, then recurse.
|
"""Search for circular deps between basedep and currdep, then recurse.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
basedep: Original dependency, top of stack.
|
basedep: Original dependency, top of stack.
|
||||||
currdep: Bottom of our current recursion, bottom of stack.
|
currdep: Bottom of our current recursion, bottom of stack.
|
||||||
oldstack: Current dependency chain.
|
visited: Nodes visited so far.
|
||||||
limit: How many more levels of recusion to go through, max.
|
cycle: Array where cycle of circular dependencies should be stored.
|
||||||
TODO(): Break RDEPEND preferentially.
|
TODO(): Break RDEPEND preferentially.
|
||||||
Returns:
|
Returns:
|
||||||
True iff circular dependencies are found.
|
True iff circular dependencies are found.
|
||||||
"""
|
"""
|
||||||
if limit == 0:
|
if currdep not in visited:
|
||||||
return
|
visited.add(currdep)
|
||||||
for dep in deps_map[currdep]["needs"]:
|
for dep in deps_map[currdep]["needs"]:
|
||||||
stack = oldstack + [dep]
|
if dep == basedep or SanitizeDep(basedep, dep, visited, cycle):
|
||||||
if basedep in deps_map[dep]["needs"] or dep == basedep:
|
cycle.insert(0, dep)
|
||||||
if dep != basedep:
|
return True
|
||||||
stack += [basedep]
|
return False
|
||||||
print "Remove cyclic dependency from:"
|
|
||||||
for i in xrange(0, len(stack) - 1):
|
|
||||||
print " %s -> %s " % (stack[i], stack[i+1])
|
|
||||||
return True
|
|
||||||
if dep not in oldstack and SanitizeDep(basedep, dep, stack, limit - 1):
|
|
||||||
return True
|
|
||||||
return
|
|
||||||
|
|
||||||
def SanitizeTree():
|
def SanitizeTree():
|
||||||
"""Remove circular dependencies up to cycle length 32."""
|
"""Remove circular dependencies."""
|
||||||
start = time.time()
|
start = time.time()
|
||||||
for basedep in deps_map:
|
for basedep in deps_map:
|
||||||
for dep in deps_map[basedep]["needs"].copy():
|
this_pkg = deps_map[basedep]
|
||||||
if deps_info[basedep]["idx"] <= deps_info[dep]["idx"]:
|
if this_pkg["action"] == "world":
|
||||||
if SanitizeDep(basedep, dep, [basedep, dep], 31):
|
# world file updates can't be involved in cycles,
|
||||||
print "Breaking", basedep, " -> ", dep
|
# and they don't have deps_info, so skip them.
|
||||||
deps_map[basedep]["needs"].remove(dep)
|
continue
|
||||||
deps_map[dep]["provides"].remove(basedep)
|
for dep in this_pkg["needs"].copy():
|
||||||
|
cycle = []
|
||||||
|
if (deps_info[basedep]["idx"] <= deps_info[dep]["idx"] and
|
||||||
|
SanitizeDep(basedep, dep, set(), cycle)):
|
||||||
|
cycle[:0] = [basedep, dep]
|
||||||
|
print "Breaking cycle:"
|
||||||
|
for i in range(len(cycle) - 1):
|
||||||
|
deptype = deps_map[cycle[i]]["needs"][cycle[i+1]]
|
||||||
|
print " %s -> %s %s" % (cycle[i], cycle[i+1], deptype)
|
||||||
|
del this_pkg["needs"][dep]
|
||||||
|
deps_map[dep]["provides"].remove(basedep)
|
||||||
seconds = time.time() - start
|
seconds = time.time() - start
|
||||||
print "Tree sanitized in %d:%04.1fs" % (seconds / 60, seconds % 60)
|
print "Tree sanitized in %d:%04.1fs" % (seconds / 60, seconds % 60)
|
||||||
|
|
||||||
@ -443,8 +479,49 @@ def GenDependencyGraph(deps_tree, deps_info):
|
|||||||
deps_map[needed_pkg]["provides"].add(bad_pkg)
|
deps_map[needed_pkg]["provides"].add(bad_pkg)
|
||||||
deps_map[bad_pkg]["needs"].add(needed_pkg)
|
deps_map[bad_pkg]["needs"].add(needed_pkg)
|
||||||
|
|
||||||
|
def WorkOnChildren(pkg):
|
||||||
|
"""Mark this package and all packages it provides as workon packages."""
|
||||||
|
|
||||||
|
this_pkg = deps_map[pkg]
|
||||||
|
if this_pkg["workon"]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
this_pkg["workon"] = True
|
||||||
|
updated = False
|
||||||
|
for w in this_pkg["provides"]:
|
||||||
|
if WorkOnChildren(w):
|
||||||
|
updated = True
|
||||||
|
|
||||||
|
if this_pkg["action"] == "nomerge":
|
||||||
|
pkgpath = deps_tree[pkg]["pkgpath"]
|
||||||
|
if pkgpath is not None:
|
||||||
|
OPTS["workon"].add(pkgpath)
|
||||||
|
updated = True
|
||||||
|
|
||||||
|
return updated
|
||||||
|
|
||||||
ReverseTree(deps_tree)
|
ReverseTree(deps_tree)
|
||||||
AddSecretDeps()
|
AddSecretDeps()
|
||||||
|
|
||||||
|
if "no-workon-deps" in OPTS:
|
||||||
|
for pkgpath in OPTS["workon"].copy():
|
||||||
|
pkg = pkgpaths[pkgpath]
|
||||||
|
deps_map[pkg]["workon"] = True
|
||||||
|
else:
|
||||||
|
mergelist_updated = False
|
||||||
|
for pkgpath in OPTS["workon"].copy():
|
||||||
|
pkg = pkgpaths[pkgpath]
|
||||||
|
if WorkOnChildren(pkg):
|
||||||
|
mergelist_updated = True
|
||||||
|
if mergelist_updated:
|
||||||
|
print "List of packages to merge updated. Recalculate dependencies..."
|
||||||
|
return None
|
||||||
|
|
||||||
|
for pkgpath in package_names:
|
||||||
|
dep_pkg = deps_map.get("original-%s" % pkgpath)
|
||||||
|
if dep_pkg and len(dep_pkg["needs"]) == 1:
|
||||||
|
dep_pkg["cmdline"] = True
|
||||||
|
|
||||||
RemoveInstalledPackages()
|
RemoveInstalledPackages()
|
||||||
SanitizeTree()
|
SanitizeTree()
|
||||||
return deps_map
|
return deps_map
|
||||||
@ -477,17 +554,17 @@ class EmergeQueue(object):
|
|||||||
self._failed = {}
|
self._failed = {}
|
||||||
|
|
||||||
def _LoadAvg(self):
|
def _LoadAvg(self):
|
||||||
loads = open('/proc/loadavg', 'r').readline().split()[:3]
|
loads = open("/proc/loadavg", "r").readline().split()[:3]
|
||||||
return ' '.join(loads)
|
return " ".join(loads)
|
||||||
|
|
||||||
def _Status(self):
|
def _Status(self):
|
||||||
"""Print status."""
|
"""Print status."""
|
||||||
seconds = time.time() - GLOBAL_START
|
seconds = time.time() - GLOBAL_START
|
||||||
print "Pending %s, Ready %s, Running %s, Retrying %s, Total %s " \
|
line = ("Pending %s, Ready %s, Running %s, Retrying %s, Total %s "
|
||||||
"[Time %dm%ds Load %s]" % (
|
"[Time %dm%ds Load %s]")
|
||||||
len(self._deps_map), len(self._emerge_queue),
|
print line % (len(self._deps_map), len(self._emerge_queue),
|
||||||
len(self._jobs), len(self._retry_queue), self._total_jobs,
|
len(self._jobs), len(self._retry_queue), self._total_jobs,
|
||||||
seconds / 60, seconds % 60, self._LoadAvg())
|
seconds / 60, seconds % 60, self._LoadAvg())
|
||||||
|
|
||||||
def _LaunchOneEmerge(self, target):
|
def _LaunchOneEmerge(self, target):
|
||||||
"""Run emerge --nodeps to do a single package install.
|
"""Run emerge --nodeps to do a single package install.
|
||||||
@ -504,20 +581,35 @@ class EmergeQueue(object):
|
|||||||
# "original-" signifies one of the packages we originally requested.
|
# "original-" signifies one of the packages we originally requested.
|
||||||
# Since we have explicitly installed the versioned package as a dep of
|
# Since we have explicitly installed the versioned package as a dep of
|
||||||
# this, we only need to tag in "world" that we are done with this
|
# this, we only need to tag in "world" that we are done with this
|
||||||
# install request. "--select -n" indicates an addition to "world"
|
# install request.
|
||||||
# without an actual install.
|
# --nodeps: Ignore dependencies -- we handle them internally.
|
||||||
|
# --noreplace: Don't replace or upgrade any packages. (In this case, the
|
||||||
|
# package is already installed, so we are just updating the
|
||||||
|
# world file.)
|
||||||
|
# --selective: Make sure that --noreplace sticks even if --selective=n is
|
||||||
|
# specified by the user on the command-line.
|
||||||
|
# NOTE: If the user specifies --oneshot on the command-line, this command
|
||||||
|
# will do nothing. That is desired, since the user requested not to
|
||||||
|
# update the world file.
|
||||||
newtarget = target.replace("original-", "")
|
newtarget = target.replace("original-", "")
|
||||||
cmdline = EmergeCommand() + " --nodeps --select --noreplace " + newtarget
|
cmdline = (EmergeCommand() + " --nodeps --selective --noreplace " +
|
||||||
|
newtarget)
|
||||||
else:
|
else:
|
||||||
# This package is a dependency of something we specifically
|
# This package is a dependency of something we specifically
|
||||||
# requested. Therefore we should install it but not allow it
|
# requested. Therefore we should install it but not allow it
|
||||||
# in the "world" file, which represents explicit intalls.
|
# in the "world" file, which represents explicit installs.
|
||||||
# "--oneshot" here will prevent it from being tagged in world.
|
# --oneshot" here will prevent it from being tagged in world.
|
||||||
cmdline = EmergeCommand() + " --nodeps --oneshot =" + target
|
cmdline = EmergeCommand() + " --nodeps --oneshot "
|
||||||
deps_info = self._deps_map[target]["deps_info"]
|
this_pkg = self._deps_map[target]
|
||||||
|
if this_pkg["workon"]:
|
||||||
|
# --usepkg=n --getbinpkg=n: Build from source
|
||||||
|
# --selective=n: Re-emerge even if package is already installed.
|
||||||
|
cmdline += "--usepkg=n --getbinpkg=n --selective=n "
|
||||||
|
cmdline += "=" + target
|
||||||
|
deps_info = this_pkg["deps_info"]
|
||||||
if deps_info["uninstall"]:
|
if deps_info["uninstall"]:
|
||||||
package = "%(pkgdir)s/%(pkgname)s-%(oldversion)s" % deps_info
|
package = "%(pkgdir)s/%(pkgname)s-%(oldversion)s" % deps_info
|
||||||
cmdline += " && %s -1C =%s" % (EmergeCommand(), package)
|
cmdline += " && %s -C =%s" % (EmergeCommand(), package)
|
||||||
|
|
||||||
print "+ %s" % cmdline
|
print "+ %s" % cmdline
|
||||||
|
|
||||||
@ -543,7 +635,7 @@ class EmergeQueue(object):
|
|||||||
def _Finish(self, target):
|
def _Finish(self, target):
|
||||||
"""Mark a target as completed and unblock dependecies."""
|
"""Mark a target as completed and unblock dependecies."""
|
||||||
for dep in self._deps_map[target]["provides"]:
|
for dep in self._deps_map[target]["provides"]:
|
||||||
self._deps_map[dep]["needs"].remove(target)
|
del self._deps_map[dep]["needs"][target]
|
||||||
if not self._deps_map[dep]["needs"]:
|
if not self._deps_map[dep]["needs"]:
|
||||||
if VERBOSE:
|
if VERBOSE:
|
||||||
print "Unblocking %s" % dep
|
print "Unblocking %s" % dep
|
||||||
@ -563,9 +655,10 @@ class EmergeQueue(object):
|
|||||||
dependency graph to merge.
|
dependency graph to merge.
|
||||||
"""
|
"""
|
||||||
secs = 0
|
secs = 0
|
||||||
|
max_jobs = EMERGE_OPTS.get("--jobs", 256)
|
||||||
while self._deps_map:
|
while self._deps_map:
|
||||||
# If we have packages that are ready, kick them off.
|
# If we have packages that are ready, kick them off.
|
||||||
if self._emerge_queue and len(self._jobs) < JOBS:
|
if self._emerge_queue and len(self._jobs) < max_jobs:
|
||||||
target = self._emerge_queue.pop(0)
|
target = self._emerge_queue.pop(0)
|
||||||
action = self._deps_map[target]["action"]
|
action = self._deps_map[target]["action"]
|
||||||
# We maintain a tree of all deps, if this doesn't need
|
# We maintain a tree of all deps, if this doesn't need
|
||||||
@ -653,25 +746,43 @@ class EmergeQueue(object):
|
|||||||
|
|
||||||
|
|
||||||
# Main control code.
|
# Main control code.
|
||||||
PACKAGE, EMERGE_ARGS, BOARD, JOBS = ParseArgs(sys.argv)
|
OPTS, EMERGE_ACTION, EMERGE_OPTS, EMERGE_FILES = ParseArgs(sys.argv)
|
||||||
|
|
||||||
if not PACKAGE:
|
if EMERGE_ACTION is not None:
|
||||||
# No packages. Pass straight through to emerge.
|
# Pass action arguments straight through to emerge
|
||||||
# Allows users to just type ./parallel_emerge --depclean
|
EMERGE_OPTS["--%s" % EMERGE_ACTION] = True
|
||||||
sys.exit(os.system(EmergeCommand()))
|
sys.exit(os.system(EmergeCommand()))
|
||||||
|
elif not EMERGE_FILES:
|
||||||
|
Usage()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
print "Starting fast-emerge."
|
print "Starting fast-emerge."
|
||||||
print " Building package %s on %s (%s)" % (PACKAGE, EMERGE_ARGS, BOARD)
|
print " Building package %s on %s" % (" ".join(EMERGE_FILES),
|
||||||
print "Running emerge to generate deps"
|
OPTS.get("board", "root"))
|
||||||
deps_output = GetDepsFromPortage(PACKAGE)
|
|
||||||
print "Processing emerge output"
|
|
||||||
dependency_tree, dependency_info = DepsToTree(deps_output)
|
|
||||||
if VERBOSE:
|
|
||||||
print "Print tree"
|
|
||||||
PrintTree(dependency_tree)
|
|
||||||
|
|
||||||
print "Generate dependency graph."
|
# If the user supplied the --workon option, we may have to run emerge twice
|
||||||
dependency_graph = GenDependencyGraph(dependency_tree, dependency_info)
|
# to generate a dependency ordering for packages that depend on the workon
|
||||||
|
# packages.
|
||||||
|
for it in range(2):
|
||||||
|
print "Running emerge to generate deps"
|
||||||
|
deps_output = GetDepsFromPortage(" ".join(EMERGE_FILES))
|
||||||
|
|
||||||
|
print "Processing emerge output"
|
||||||
|
dependency_tree, dependency_info = DepsToTree(deps_output)
|
||||||
|
|
||||||
|
if VERBOSE:
|
||||||
|
print "Print tree"
|
||||||
|
PrintTree(dependency_tree)
|
||||||
|
|
||||||
|
print "Generate dependency graph."
|
||||||
|
dependency_graph = GenDependencyGraph(dependency_tree, dependency_info,
|
||||||
|
EMERGE_FILES)
|
||||||
|
|
||||||
|
if dependency_graph is not None:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print "Can't crack cycle"
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if VERBOSE:
|
if VERBOSE:
|
||||||
PrintDepsMap(dependency_graph)
|
PrintDepsMap(dependency_graph)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user