Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(80)

Side by Side Diff: parallel_emerge

Issue 2886010: Robustify package upgrades and dependency checking. (Closed) Base URL: ssh://git@chromiumos-git/crosutils.git
Patch Set: Fix nits Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python2.6 1 #!/usr/bin/python2.6
2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Program to run emerge in parallel, for significant speedup. 6 """Program to run emerge in parallel, for significant speedup.
7 7
8 Usage: 8 Usage:
9 ./parallel_emerge --board=BOARD [emerge args] package 9 ./parallel_emerge --board=BOARD [emerge args] package
10 10
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
131 131
132 def GetDepsFromPortage(package): 132 def GetDepsFromPortage(package):
133 """Get dependency tree info by running emerge. 133 """Get dependency tree info by running emerge.
134 134
135 Run 'emerge -p --debug package', and get a text output of all deps. 135 Run 'emerge -p --debug package', and get a text output of all deps.
136 TODO(): Put dep caclation in a library, as cros_extract_deps 136 TODO(): Put dep caclation in a library, as cros_extract_deps
137 also uses this code. 137 also uses this code.
138 Args: 138 Args:
139 package: string containing the packages to build. 139 package: string containing the packages to build.
140 Returns: 140 Returns:
141 text output of emege -p --debug, which can be processed elsewhere. 141 text output of emerge -p --debug, which can be processed elsewhere.
142 """ 142 """
143 print "Calculating deps for package %s" % package 143 print "Calculating deps for package %s" % package
144 cmdline = EmergeCommand() + " -p --debug " + package 144 cmdline = EmergeCommand() + " -p --debug --color=n " + package
145 print "+ %s" % cmdline 145 print "+ %s" % cmdline
146 146
147 # Store output in a temp file as it is too big for a unix pipe. 147 # Store output in a temp file as it is too big for a unix pipe.
148 stderr_buffer = tempfile.TemporaryFile() 148 stderr_buffer = tempfile.TemporaryFile()
149 stdout_buffer = tempfile.TemporaryFile() 149 stdout_buffer = tempfile.TemporaryFile()
150 # Launch the subprocess. 150 # Launch the subprocess.
151 start = time.time()
151 depsproc = subprocess.Popen(shlex.split(cmdline), stderr=stderr_buffer, 152 depsproc = subprocess.Popen(shlex.split(cmdline), stderr=stderr_buffer,
152 stdout=stdout_buffer, bufsize=64*1024) 153 stdout=stdout_buffer, bufsize=64*1024)
153
154 # Wait for this to complete.
155 seconds = 0
156 while depsproc.poll() is not None:
157 seconds += 1
158 time.sleep(1)
159 if seconds % 5 == 0:
160 print ".",
161 print " done"
162
163 print "Deps calculated in %d:%02ds" % (seconds / 60, seconds % 60)
164
165 depsproc.wait() 154 depsproc.wait()
155 seconds = time.time() - start
156 print "Deps calculated in %d:%04.1fs" % (seconds / 60, seconds % 60)
166 stderr_buffer.seek(0) 157 stderr_buffer.seek(0)
167 stderr_raw = stderr_buffer.read() 158 stderr_raw = stderr_buffer.read()
168 info_start = stderr_raw.find("digraph") 159 info_start = stderr_raw.find("digraph")
160 stdout_buffer.seek(0)
161 stdout_raw = stdout_buffer.read()
162 lines = []
169 if info_start != -1: 163 if info_start != -1:
170 stdout = stderr_raw[info_start:] 164 lines = stderr_raw[info_start:].split("\n")
171 else: 165 lines.extend(stdout_raw.split("\n"))
172 stdout_buffer.seek(0)
173 stdout_raw = stdout_buffer.read()
174 stdout = stderr_raw + stdout_raw
175 if VERBOSE or depsproc.returncode != 0: 166 if VERBOSE or depsproc.returncode != 0:
176 print stdout 167 output = stderr_raw + stdout_raw
168 print output
177 if depsproc.returncode != 0: 169 if depsproc.returncode != 0:
178 print "Failed to generate deps" 170 print "Failed to generate deps"
179 sys.exit(1) 171 sys.exit(1)
180 172
181 lines = stdout.split("\n")
182 return lines 173 return lines
183 174
184 175
185 def DepsToTree(lines): 176 def DepsToTree(lines):
186 """Regex the emerge --tree output to generate a nested dict of dependencies. 177 """Regex the output from 'emerge --debug' to generate a nested dict of deps.
187 178
188 Args: 179 Args:
189 lines: text dump from 'emerge -p --tree package' 180 lines: output from 'emerge -p --debug package'
190 Returns: 181 Returns:
191 dep_tree: nested dict of dependencies, as specified by emerge. 182 dep_tree: nested dict of dependencies, as specified by emerge.
192 there may be dupes, or circular deps. 183 there may be dupes, or circular deps.
193 184
194 We need to regex lines as follows: 185 We need to regex lines as follows:
195 hard-host-depends depends on 186 hard-host-depends depends on
196 ('ebuild', '/', 'dev-lang/swig-1.3.36', 'merge') depends on 187 ('ebuild', '/', 'dev-lang/swig-1.3.36', 'merge') depends on
197 ('ebuild', '/', 'dev-lang/perl-5.8.8-r8', 'merge') (buildtime) 188 ('ebuild', '/', 'dev-lang/perl-5.8.8-r8', 'merge') (buildtime)
198 ('binary', '/.../rootfs/', 'sys-auth/policykit-0.9-r1', 'merge') depends on 189 ('binary', '/.../rootfs/', 'sys-auth/policykit-0.9-r1', 'merge') depends on
199 ('binary', '/.../rootfs/', 'x11-misc/xbitmaps-1.1.0', 'merge') (no children) 190 ('binary', '/.../rootfs/', 'x11-misc/xbitmaps-1.1.0', 'merge') (no children)
200 """ 191 """
201 192
202 re_deps = re.compile(r"(?P<indent>\W*)\(\'(?P<pkgtype>\w+)\', " 193 re_deps = re.compile(r"(?P<indent>\W*)\(\'(?P<pkgtype>\w+)\', "
203 r"\'(?P<destination>[\w/\.-]+)\'," 194 r"\'(?P<destination>[\w/\.-]+)\',"
204 r" \'(?P<pkgdir>[\w\+-]+)/(?P<pkgname>[\w\+-]+)-" 195 r" \'(?P<pkgdir>[\w\+-]+)/(?P<pkgname>[\w\+-]+)-"
205 r"(?P<version>\d+[\w\.-]*)\', \'(?P<action>\w+)\'\) " 196 r"(?P<version>\d+[\w\.-]*)\', \'(?P<action>\w+)\'\) "
206 r"(?P<deptype>(depends on|\(.*\)))") 197 r"(?P<deptype>(depends on|\(.*\)))")
207 re_origdeps = re.compile(r"(?P<pkgname>[\w\+/-]+) depends on") 198 re_origdeps = re.compile(r"(?P<pkgname>[\w\+/-]+) depends on")
199 re_installed_package = re.compile(
200 r"\[(?P<desc>[^\]]*)\] "
201 r"(?P<pkgdir>[\w\+-]+)/"
202 r"(?P<pkgname>[\w\+-]+)-"
203 r"(?P<version>\d+[\w\.-]*)( \["
204 r"(?P<oldversion>\d+[\w\.-]*)\])?"
205 )
208 re_failed = re.compile(r".*depends on.*") 206 re_failed = re.compile(r".*depends on.*")
209
210 deps_tree = {} 207 deps_tree = {}
211 deps_stack = [] 208 deps_stack = []
209 deps_info = {}
212 for line in lines: 210 for line in lines:
213 m = re_deps.match(line) 211 m = re_deps.match(line)
214 m_orig = re_origdeps.match(line) 212 m_orig = re_origdeps.match(line)
213 m_installed = re_installed_package.match(line)
215 if m: 214 if m:
216 pkgname = m.group("pkgname") 215 pkgname = m.group("pkgname")
217 pkgdir = m.group("pkgdir") 216 pkgdir = m.group("pkgdir")
218 pkgtype = m.group("pkgtype") 217 pkgtype = m.group("pkgtype")
219 indent = m.group("indent") 218 indent = m.group("indent")
220 doins = m.group("action") 219 doins = m.group("action")
221 deptype = m.group("deptype") 220 deptype = m.group("deptype")
222 depth = 1 221 depth = 1
223 if not indent: 222 if not indent:
224 depth = 0 223 depth = 0
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
276 updatedep.setdefault(pkgname, {}) 275 updatedep.setdefault(pkgname, {})
277 updatedep[pkgname].setdefault("deps", {}) 276 updatedep[pkgname].setdefault("deps", {})
278 # Add the type of dep. 277 # Add the type of dep.
279 updatedep[pkgname].setdefault("action", "world") 278 updatedep[pkgname].setdefault("action", "world")
280 updatedep[pkgname].setdefault("deptype", "normal") 279 updatedep[pkgname].setdefault("deptype", "normal")
281 280
282 # Drop any obsolete stack entries. 281 # Drop any obsolete stack entries.
283 deps_stack = deps_stack[0:depth] 282 deps_stack = deps_stack[0:depth]
284 # Add ourselves to the end of the stack. 283 # Add ourselves to the end of the stack.
285 deps_stack.append(pkgname) 284 deps_stack.append(pkgname)
285 elif m_installed:
286 pkgname = m_installed.group("pkgname")
287 pkgdir = m_installed.group("pkgdir")
288 version = m_installed.group("version")
289 oldversion = m_installed.group("oldversion")
290 desc = m_installed.group("desc")
291 uninstall = False
292 if oldversion and (desc.find("U") != -1 or desc.find("D") != -1):
293 uninstall = True
294 fullpkg = "%s/%s-%s" % (pkgdir, pkgname, version)
295 deps_info[fullpkg] = {"idx": len(deps_info),
296 "pkgdir": pkgdir,
297 "pkgname": pkgname,
298 "oldversion": oldversion,
299 "uninstall": uninstall}
286 else: 300 else:
287 # Is this a package that failed to match uor huge regex? 301 # Is this a package that failed to match our huge regex?
288 m = re_failed.match(line) 302 m = re_failed.match(line)
289 if m: 303 if m:
290 print "FAIL: Couldn't understand line:" 304 print "FAIL: Couldn't understand line:"
291 print line 305 print line
292 sys.exit(1) 306 sys.exit(1)
293 307
294 return deps_tree 308 return deps_tree, deps_info
295 309
296 310
297 def PrintTree(deps, depth=""): 311 def PrintTree(deps, depth=""):
298 """Print the deps we have seen in the emerge output. 312 """Print the deps we have seen in the emerge output.
299 313
300 Args: 314 Args:
301 deps: dependency tree structure. 315 deps: dependency tree structure.
302 depth: allows printing the tree recursively, with indentation. 316 depth: allows printing the tree recursively, with indentation.
303 """ 317 """
304 for entry in deps: 318 for entry in deps:
305 action = deps[entry]["action"] 319 action = deps[entry]["action"]
306 print "%s %s (%s)" % (depth, entry, action) 320 print "%s %s (%s)" % (depth, entry, action)
307 PrintTree(deps[entry]["deps"], depth=depth + " ") 321 PrintTree(deps[entry]["deps"], depth=depth + " ")
308 322
309 323
310 def GenDependencyGraph(deps_tree): 324 def GenDependencyGraph(deps_tree, deps_info):
311 """Generate a doubly linked dependency graph. 325 """Generate a doubly linked dependency graph.
312 326
313 Args: 327 Args:
314 deps_tree: dependency tree structure. 328 deps_tree: dependency tree structure.
329 deps_info: more info on the dependencies.
315 Returns: 330 Returns:
316 Deps graph in the form of a dict of packages, with each package 331 Deps graph in the form of a dict of packages, with each package
317 specifying a "needs" list and "provides" list. 332 specifying a "needs" list and "provides" list.
318 """ 333 """
319 deps_map = {} 334 deps_map = {}
320 335
321 def ReverseTree(packages): 336 def ReverseTree(packages):
322 """Convert tree to digraph. 337 """Convert tree to digraph.
323 338
324 Take the tree of package -> requirements and reverse it to a digraph of 339 Take the tree of package -> requirements and reverse it to a digraph of
325 buildable packages -> packages they unblock 340 buildable packages -> packages they unblock
326 Args: 341 Args:
327 packages: tree(s) of dependencies 342 packages: tree(s) of dependencies
328 Returns: 343 Returns:
329 unsanitized digraph 344 unsanitized digraph
330 """ 345 """
331 for pkg in packages: 346 for pkg in packages:
332 action = packages[pkg]["action"] 347 action = packages[pkg]["action"]
333 this_pkg = deps_map.setdefault( 348 this_pkg = deps_map.setdefault(
334 pkg, {"needs": {}, "provides": set(), "action": "nomerge"}) 349 pkg, {"needs": set(), "provides": set(), "action": "nomerge"})
335 if action != "nomerge": 350 if action != "nomerge":
336 this_pkg["action"] = action 351 this_pkg["action"] = action
352 this_pkg["deps_info"] = deps_info.get(pkg)
337 ReverseTree(packages[pkg]["deps"]) 353 ReverseTree(packages[pkg]["deps"])
338 for dep, dep_item in packages[pkg]["deps"].items(): 354 for dep, dep_item in packages[pkg]["deps"].items():
339 dep_pkg = deps_map[dep] 355 dep_pkg = deps_map[dep]
340 dep_type = dep_item["deptype"] 356 dep_type = dep_item["deptype"]
341 if dep_type == "(runtime_post)": 357 if dep_type != "(runtime_post)":
342 dep_pkg["needs"][pkg] = dep_type
343 this_pkg["provides"].add(dep)
344 else:
345 dep_pkg["provides"].add(pkg) 358 dep_pkg["provides"].add(pkg)
346 this_pkg["needs"][dep] = dep_type 359 this_pkg["needs"].add(dep)
360
361 def RemoveInstalledPackages():
362 """Remove installed packages, propagating dependencies"""
363
364 rm_pkgs = set(deps_map.keys()) - set(deps_info.keys())
365 for pkg in rm_pkgs:
366 this_pkg = deps_map[pkg]
367 needs = this_pkg["needs"]
368 provides = this_pkg["provides"]
369 for dep in needs:
370 dep_provides = deps_map[dep]["provides"]
371 dep_provides.update(provides)
372 dep_provides.discard(pkg)
373 dep_provides.discard(dep)
374 for target in provides:
375 target_needs = deps_map[target]["needs"]
376 target_needs.update(needs)
377 target_needs.discard(pkg)
378 target_needs.discard(target)
379 del deps_map[pkg]
380
347 381
348 def SanitizeDep(basedep, currdep, oldstack, limit): 382 def SanitizeDep(basedep, currdep, oldstack, limit):
349 """Remove any circular dependencies between basedep, currdep, then recurse. 383 """Search for circular deps between basedep and currdep, then recurse.
350 384
351 Args: 385 Args:
352 basedep: original dependency, top of stack. 386 basedep: original dependency, top of stack.
353 currdep: bottom of our current recursion, bottom of stack. 387 currdep: bottom of our current recursion, bottom of stack.
354 oldstack: current dependency chain. 388 oldstack: current dependency chain.
355 limit: how many more levels of recusion to go through, max. 389 limit: how many more levels of recusion to go through, max.
356 TODO(): Break PDEPEND preferentially, then RDEPEND. Also extract emerge 390 TODO(): Break RDEPEND preferentially.
357 linear ordering and break cycles on default emerge linear order. 391 Returns:
392 True iff circular dependencies are found.
358 """ 393 """
359 if limit == 0: 394 if limit == 0:
360 return 395 return
361 for dep in deps_map[currdep]["needs"]: 396 for dep in deps_map[currdep]["needs"]:
362 stack = oldstack + [dep] 397 stack = oldstack + [dep]
363 if basedep in deps_map[dep]["needs"]: 398 if basedep in deps_map[dep]["needs"] or dep == basedep:
399 if dep != basedep:
400 stack += [basedep]
364 print "Remove cyclic dependency from:" 401 print "Remove cyclic dependency from:"
365 for i in xrange(0, len(stack) - 1): 402 for i in xrange(0, len(stack) - 1):
366 print " %s (%s)-> %s " % ( 403 print " %s -> %s " % (stack[i], stack[i+1])
367 stack[i], deps_map[stack[i]]["needs"][stack[i+1]], stack[i+1]) 404 return True
368 del deps_map[dep]["needs"][basedep] 405 if dep not in oldstack and SanitizeDep(basedep, dep, stack, limit - 1):
369 deps_map[basedep]["provides"].remove(dep) 406 return True
370 SanitizeDep(basedep, dep, stack, limit - 1) 407 return
371 408
372 def SanitizeTree(): 409 def SanitizeTree():
373 """Remove circular dependencies up to cycle length 8.""" 410 """Remove circular dependencies up to cycle length 32."""
374 for dep in deps_map: 411 start = time.time()
375 SanitizeDep(dep, dep, [dep], 8) 412 for basedep in deps_map:
413 for dep in deps_map[basedep]["needs"].copy():
414 if deps_info[basedep]["idx"] <= deps_info[dep]["idx"]:
415 if SanitizeDep(basedep, dep, [basedep, dep], 31):
416 print "Breaking", basedep, " -> ", dep
417 deps_map[basedep]["needs"].remove(dep)
418 deps_map[dep]["provides"].remove(basedep)
419 seconds = time.time() - start
420 print "Tree sanitized in %d:%04.1fs" % (seconds / 60, seconds % 60)
376 421
377 def AddSecretDeps(): 422 def AddSecretDeps():
378 """Find these tagged packages and add extra dependencies. 423 """Find these tagged packages and add extra dependencies.
379 424
380 For debugging dependency problems. 425 For debugging dependency problems.
381 """ 426 """
382 for bad in secret_deps: 427 for bad in secret_deps:
383 needed = secret_deps[bad] 428 needed = secret_deps[bad]
384 bad_pkg = None 429 bad_pkg = None
385 needed_pkg = None 430 needed_pkg = None
386 for dep in deps_map: 431 for dep in deps_map:
387 if dep.find(bad) != -1: 432 if dep.find(bad) != -1:
388 bad_pkg = dep 433 bad_pkg = dep
389 if dep.find(needed) != -1: 434 if dep.find(needed) != -1:
390 needed_pkg = dep 435 needed_pkg = dep
391 if bad_pkg and needed_pkg: 436 if bad_pkg and needed_pkg:
392 deps_map[needed_pkg]["provides"].add(bad_pkg) 437 deps_map[needed_pkg]["provides"].add(bad_pkg)
393 deps_map[bad_pkg]["needs"][needed_pkg] = "(manually forced)" 438 deps_map[bad_pkg]["needs"].add(needed_pkg)
394 439
395 ReverseTree(deps_tree) 440 ReverseTree(deps_tree)
396 AddSecretDeps() 441 AddSecretDeps()
442 RemoveInstalledPackages()
397 SanitizeTree() 443 SanitizeTree()
398 return deps_map 444 return deps_map
399 445
400 446
401 def PrintDepsMap(deps_map): 447 def PrintDepsMap(deps_map):
402 """Print dependency graph, for each package list it's prerequisites.""" 448 """Print dependency graph, for each package list it's prerequisites."""
403 for i in deps_map: 449 for i in deps_map:
404 print "%s: (%s) needs" % (i, deps_map[i]["action"]) 450 print "%s: (%s) needs" % (i, deps_map[i]["action"])
405 for j, dep_type in deps_map[i]["needs"].items(): 451 for j in deps_map[i]["needs"]:
406 print " %s ( %s )" % (j, dep_type) 452 print " %s" % (j)
407 453
408 454
409 class EmergeQueue(object): 455 class EmergeQueue(object):
410 """Class to schedule emerge jobs according to a dependency graph.""" 456 """Class to schedule emerge jobs according to a dependency graph."""
411 457
412 def __init__(self, deps_map): 458 def __init__(self, deps_map):
413 # Store the dependency graph. 459 # Store the dependency graph.
414 self._deps_map = deps_map 460 self._deps_map = deps_map
415 # Initialize the runnable queue to empty. 461 # Initialize the runnable queue to empty.
416 self._jobs = [] 462 self._jobs = []
417 # List of total package installs represented in deps_map. 463 # List of total package installs represented in deps_map.
418 install_jobs = [x for x in deps_map if deps_map[x]["action"] == "merge"] 464 install_jobs = [x for x in deps_map if deps_map[x]["action"] == "merge"]
419 self._total_jobs = len(install_jobs) 465 self._total_jobs = len(install_jobs)
420 466
421 # Initialize the ready queue, these are jobs with no unmet dependencies. 467 # Initialize the ready queue, these are jobs with no unmet dependencies.
422 self._emerge_queue = [x for x in deps_map if not deps_map[x]["needs"]] 468 self._emerge_queue = [x for x in deps_map if not deps_map[x]["needs"]]
423 # Initialize the failed queue to empty. 469 # Initialize the failed queue to empty.
424 self._retry_queue = [] 470 self._retry_queue = []
425 self._failed = {} 471 self._failed = {}
426 472
427 def _Status(self): 473 def _Status(self):
428 """Print status.""" 474 """Print status."""
429 print "Pending %s, Ready %s, Running %s, Failed %s, Total %s" % ( 475 print "Pending %s, Ready %s, Running %s, Retrying %s, Total %s" % (
430 len(self._deps_map), len(self._emerge_queue), 476 len(self._deps_map), len(self._emerge_queue),
431 len(self._jobs), len(self._failed), self._total_jobs) 477 len(self._jobs), len(self._retry_queue), self._total_jobs)
432 478
433 def _LaunchOneEmerge(self, target): 479 def _LaunchOneEmerge(self, target):
434 """Run emerge --nodeps to do a single package install. 480 """Run emerge --nodeps to do a single package install.
435 481
436 If this is a pseudopackage, that means we're done, and can select in in the 482 If this is a pseudopackage, that means we're done, and can select in in the
437 world file. 483 world file.
438 Args: 484 Args:
439 target: the full package name of the package to install. 485 target: the full package name of the package to install.
440 eg. "sys-apps/portage-2.17" 486 eg. "sys-apps/portage-2.17"
441 Returns: 487 Returns:
442 triplet containing (target name, subprocess object, output buffer object) 488 triplet containing (target name, subprocess object, output buffer object)
443 """ 489 """
444 if target.startswith("original-"): 490 if target.startswith("original-"):
445 # "original-" signifies one of the packages we originally requested. 491 # "original-" signifies one of the packages we originally requested.
446 # Since we have explicitly installed the versioned package as a dep of 492 # Since we have explicitly installed the versioned package as a dep of
447 # this, we only need to tag in "world" that we are done with this 493 # this, we only need to tag in "world" that we are done with this
448 # install request. "--select -n" indicates an addition to "world" 494 # install request. "--select -n" indicates an addition to "world"
449 # without an actual install. 495 # without an actual install.
450 newtarget = target.replace("original-", "") 496 newtarget = target.replace("original-", "")
451 cmdline = EmergeCommand() + " --nodeps --select --noreplace " + newtarget 497 cmdline = EmergeCommand() + " --nodeps --select --noreplace " + newtarget
452 else: 498 else:
453 # This package is a dependency of something we specifically 499 # This package is a dependency of something we specifically
454 # requested. Therefore we should install it but not allow it 500 # requested. Therefore we should install it but not allow it
455 # in the "world" file, which represents explicit intalls. 501 # in the "world" file, which represents explicit intalls.
456 # "--oneshot" here will prevent it from being tagged in world. 502 # "--oneshot" here will prevent it from being tagged in world.
457 cmdline = EmergeCommand() + " --nodeps --oneshot =" + target 503 cmdline = EmergeCommand() + " --nodeps --oneshot =" + target
458 if VERBOSE: 504 deps_info = self._deps_map[target]["deps_info"]
459 print "running %s" % cmdline 505 if deps_info["uninstall"]:
506 package = "%(pkgdir)s/%(pkgname)s-%(oldversion)s" % deps_info
507 cmdline += " && %s -1C =%s" % (EmergeCommand(), package)
508
509 print "+ %s" % cmdline
460 510
461 # Store output in a temp file as it is too big for a unix pipe. 511 # Store output in a temp file as it is too big for a unix pipe.
462 stdout_buffer = tempfile.TemporaryFile() 512 stdout_buffer = tempfile.TemporaryFile()
463 # Modify the environment to disable locking. 513 # Modify the environment to disable locking.
464 portage_env = os.environ.copy() 514 portage_env = os.environ.copy()
465 portage_env["PORTAGE_LOCKS"] = "false" 515 portage_env["PORTAGE_LOCKS"] = "false"
516 portage_env["UNMERGE_DELAY"] = "0"
466 # Autoclean rummages around in the portage database and uninstalls 517 # Autoclean rummages around in the portage database and uninstalls
467 # old packages. Definitely not necessary for build_image. However 518 # old packages. Definitely not necessary for build_image. However
468 # it may be necessary for incremental build_packages. It may also 519 # it may be necessary for incremental build_packages. It may also
469 # not be parallel safe. 520 # not be parallel safe.
470 if not AUTOCLEAN: 521 if not AUTOCLEAN:
471 portage_env["AUTOCLEAN"] = "no" 522 portage_env["AUTOCLEAN"] = "no"
472 # Launch the subprocess. 523 # Launch the subprocess.
473 emerge_proc = subprocess.Popen( 524 emerge_proc = subprocess.Popen(
474 shlex.split(cmdline), stdout=stdout_buffer, 525 cmdline, shell=True, stdout=stdout_buffer,
475 stderr=subprocess.STDOUT, bufsize=64*1024, env=portage_env) 526 stderr=subprocess.STDOUT, bufsize=64*1024, env=portage_env)
476 527
477 return (target, emerge_proc, stdout_buffer) 528 return (target, emerge_proc, stdout_buffer)
478 529
479 def _Finish(self, target): 530 def _Finish(self, target):
480 """Mark a target as completed and unblock dependecies.""" 531 """Mark a target as completed and unblock dependecies."""
481 for dep in self._deps_map[target]["provides"]: 532 for dep in self._deps_map[target]["provides"]:
482 del self._deps_map[dep]["needs"][target] 533 self._deps_map[dep]["needs"].remove(target)
483 if not self._deps_map[dep]["needs"]: 534 if not self._deps_map[dep]["needs"]:
484 if VERBOSE: 535 if VERBOSE:
485 print "Unblocking %s" % dep 536 print "Unblocking %s" % dep
486 self._emerge_queue.append(dep) 537 self._emerge_queue.append(dep)
487 self._deps_map.pop(target) 538 self._deps_map.pop(target)
488 539
489 def _Retry(self): 540 def _Retry(self):
490 if self._retry_queue: 541 if self._retry_queue:
491 target = self._retry_queue.pop(0) 542 target = self._retry_queue.pop(0)
492 self._emerge_queue.append(target) 543 self._emerge_queue.append(target)
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
584 635
585 636
586 # Main control code. 637 # Main control code.
587 print "Starting fast-emerge." 638 print "Starting fast-emerge."
588 PACKAGE, EMERGE_ARGS, BOARD = ParseArgs(sys.argv) 639 PACKAGE, EMERGE_ARGS, BOARD = ParseArgs(sys.argv)
589 print " Building package %s on %s (%s)" % (PACKAGE, EMERGE_ARGS, BOARD) 640 print " Building package %s on %s (%s)" % (PACKAGE, EMERGE_ARGS, BOARD)
590 641
591 print "Running emerge to generate deps" 642 print "Running emerge to generate deps"
592 deps_output = GetDepsFromPortage(PACKAGE) 643 deps_output = GetDepsFromPortage(PACKAGE)
593 print "Processing emerge output" 644 print "Processing emerge output"
594 dependency_tree = DepsToTree(deps_output) 645 dependency_tree, dependency_info = DepsToTree(deps_output)
595 if VERBOSE: 646 if VERBOSE:
596 print "Print tree" 647 print "Print tree"
597 PrintTree(dependency_tree) 648 PrintTree(dependency_tree)
598 649
599 print "Generate dependency graph." 650 print "Generate dependency graph."
600 dependency_graph = GenDependencyGraph(dependency_tree) 651 dependency_graph = GenDependencyGraph(dependency_tree, dependency_info)
601 652
602 if VERBOSE: 653 if VERBOSE:
603 PrintDepsMap(dependency_graph) 654 PrintDepsMap(dependency_graph)
604 655
605 # Run the queued emerges. 656 # Run the queued emerges.
606 scheduler = EmergeQueue(dependency_graph) 657 scheduler = EmergeQueue(dependency_graph)
607 scheduler.Run() 658 scheduler.Run()
608 659
609 print "Done" 660 print "Done"
610 661
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698