|
|
@@ -8,7 +8,9 @@ typedef Entry = { |
| 8 |
8 |
ver: Version, |
| 9 |
9 |
path: String, |
| 10 |
10 |
repo: String, |
| 11 |
|
- deps: Array<String> |
|
11 |
+ slot: String, |
|
12 |
+ deps: Array<String>, |
|
13 |
+ subslot: String |
| 12 |
14 |
} |
| 13 |
15 |
|
| 14 |
16 |
typedef Status = { |
|
|
@@ -195,11 +197,62 @@ class Main { |
| 195 |
197 |
|
| 196 |
198 |
static function installedSatisfies(a: Atom): Bool { |
| 197 |
199 |
for (i in vdb.installed(a.cat, a.pkg)) { |
|
200 |
+ var info = Slots.installedSlot(vdb.root, a.cat, a.pkg + "-" + i.ver.raw); |
|
201 |
+ if (AtomParser.matches(a, i.ver) && AtomParser.slotMatches(a, info.slot, info.subslot)) return true; |
| 198 |
202 |
if (AtomParser.matches(a, i.ver)) return true; |
| 199 |
203 |
} |
| 200 |
204 |
return false; |
| 201 |
205 |
} |
| 202 |
206 |
|
|
207 |
+ static var blockerList: Array<{atom: Atom, strong: Bool, by: String}> = []; |
|
208 |
+ |
|
209 |
+ static function checkBlockers(): Bool { |
|
210 |
+ var planned = new Map<String, Bool>(); |
|
211 |
+ for (e in plan) planned.set(e.cat + "/" + e.pkg, true); |
|
212 |
+ |
|
213 |
+ var ok = true; |
|
214 |
+ for (b in blockerList) { |
|
215 |
+ var label = b.atom.cat + "/" + b.atom.pkg; |
|
216 |
+ if (planned.exists(label)) continue; |
|
217 |
+ for (i in vdb.installed(b.atom.cat, b.atom.pkg)) { |
|
218 |
+ var info = Slots.installedSlot(vdb.root, b.atom.cat, b.atom.pkg + "-" + i.ver.raw); |
|
219 |
+ if (!AtomParser.matches(b.atom, i.ver) || !AtomParser.slotMatches(b.atom, info.slot, info.subslot)) continue; |
|
220 |
+ |
|
221 |
+ ok = false; |
|
222 |
+ var kind = b.strong ? "!!" : "!"; |
|
223 |
+ Sys.println(c("1;31", ">>> blocked: " + b.by + " conflicts with installed " + label + "-" + i.ver.raw + " (" + kind + " blocker)")); |
|
224 |
+ } |
|
225 |
+ } |
|
226 |
+ if (!ok) Sys.println(c("1;31", ">>> Resolve the blocker first. Unmerging installed packages is not supported yet.")); |
|
227 |
+ return ok; |
|
228 |
+ } |
|
229 |
+ |
|
230 |
+ static var reportedCycles = new Map<String, Bool>(); |
|
231 |
+ |
|
232 |
+ static function reportCycle(key: String): Void { |
|
233 |
+ var start = chain.indexOf(key); |
|
234 |
+ var loop = chain.slice(start).concat([key]); |
|
235 |
+ var text = loop.join(" -> "); |
|
236 |
+ if (reportedCycles.exists(text)) return; |
|
237 |
+ reportedCycles.set(text, true); |
|
238 |
+ warn("circular dependency: " + text + " (cut here; merge order inside the loop may be wrong)"); |
|
239 |
+ } |
|
240 |
+ |
|
241 |
+ static function slotOf(repoName: String, path: String): {slot: String, subslot: String} { |
|
242 |
+ for (r in repos.list) { |
|
243 |
+ if (r.name == repoName) return r.tree.slotInfo(path); |
|
244 |
+ } |
|
245 |
+ return {slot: "0", subslot: "0"}; |
|
246 |
+ } |
|
247 |
+ |
|
248 |
+ static function installedSubslot(cat: String, pkg: String, slot: String): Null<String> { |
|
249 |
+ for (i in vdb.installed(cat, pkg)) { |
|
250 |
+ var info = Slots.installedSlot(vdb.root, cat, pkg + "-" + i.ver.raw); |
|
251 |
+ if (info.slot == slot) return info.subslot; |
|
252 |
+ } |
|
253 |
+ return null; |
|
254 |
+ } |
|
255 |
+ |
| 203 |
256 |
static function hostSatisfies(a: Atom): Bool { |
| 204 |
257 |
for (i in hostVdb.installed(a.cat, a.pkg)) { |
| 205 |
258 |
if (AtomParser.matches(a, i.ver)) return true; |
|
|
@@ -236,7 +289,7 @@ class Main { |
| 236 |
289 |
|
| 237 |
290 |
if (hit == null || topInst.ver.compare(hit.ver) >= 0) { |
| 238 |
291 |
if (AtomParser.matches(a, topInst.ver)) { |
| 239 |
|
- var matchedRepo = null; |
|
292 |
+ var matchedRepo: Dynamic = null; |
| 240 |
293 |
for (r in repos.list) { |
| 241 |
294 |
if (r.name == topInst.repo) { |
| 242 |
295 |
matchedRepo = r; |
|
|
@@ -265,7 +318,10 @@ class Main { |
| 265 |
318 |
} |
| 266 |
319 |
|
| 267 |
320 |
var key = a.cat + "/" + a.pkg + "-" + targetVer.raw; |
| 268 |
|
- if (seen.exists(key)) return; |
|
321 |
+ if (seen.exists(key)) { |
|
322 |
+ if (chain.indexOf(key) >= 0) reportCycle(key); |
|
323 |
+ return; |
|
324 |
+ } |
| 269 |
325 |
seen.set(key, true); |
| 270 |
326 |
|
| 271 |
327 |
if (chain.length > 150) { |
|
|
@@ -304,7 +360,17 @@ class Main { |
| 304 |
360 |
} |
| 305 |
361 |
|
| 306 |
362 |
chain.pop(); |
| 307 |
|
- plan.push({cat: a.cat, pkg: a.pkg, ver: targetVer, path: targetPath, repo: targetRepo, deps: depLabels}); |
|
363 |
+ var si = slotOf(targetRepo, targetPath); |
|
364 |
+ plan.push({cat: a.cat, pkg: a.pkg, ver: targetVer, path: targetPath, repo: targetRepo, deps: depLabels, slot: si.slot, subslot: si.subslot}); |
|
365 |
+ |
|
366 |
+ var by = a.cat + "/" + a.pkg + "-" + targetVer.raw; |
|
367 |
+ var blocked = Dependencies.blockers(Dependencies.parse(tree.runtimeSpec(targetPath)), flags) |
|
368 |
+ .concat(Dependencies.blockers(Dependencies.parse(tree.buildSpec(targetPath)), flags)); |
|
369 |
+ for (b in blocked) blockerList.push({atom: b.atom, strong: b.strong, by: by}); |
|
370 |
+ |
|
371 |
+ for (d in Dependencies.eval(Dependencies.parse(tree.variable(targetPath, "PDEPEND")), flags, chooser)) { |
|
372 |
+ resolve(d, d.cat + "/" + d.pkg, false); |
|
373 |
+ } |
| 308 |
374 |
} |
| 309 |
375 |
|
| 310 |
376 |
static function upgradeAvailable(a: Atom): Bool { |
|
|
@@ -327,6 +393,25 @@ class Main { |
| 327 |
393 |
return true; |
| 328 |
394 |
} |
| 329 |
395 |
|
|
396 |
+ static function rebuildSubslotDependents(): Void { |
|
397 |
+ for (e in plan.copy()) { |
|
398 |
+ var old = installedSubslot(e.cat, e.pkg, e.slot); |
|
399 |
+ if (old == null || old == e.subslot) continue; |
|
400 |
+ |
|
401 |
+ for (dep in Slots.dependentsWithSubslotOp(vdb.root, e.cat, e.pkg)) { |
|
402 |
+ var a = AtomParser.parse(dep); |
|
403 |
+ if (a == null) continue; |
|
404 |
+ clearProgress(); |
|
405 |
+ Sys.println(c("1;33", ">>> " + dep + " uses " + e.cat + "/" + e.pkg + ":= and must be rebuilt (subslot " + old + " -> " + e.subslot + ")")); |
|
406 |
+ try { |
|
407 |
+ resolve(a, dep, true); |
|
408 |
+ } catch (x: Dynamic) { |
|
409 |
+ Sys.println(c("1;31", ">>> could not plan rebuild of " + dep + ": " + x)); |
|
410 |
+ } |
|
411 |
+ } |
|
412 |
+ } |
|
413 |
+ } |
|
414 |
+ |
| 330 |
415 |
static function chooser(alts: Array<Array<Atom>>): Int { |
| 331 |
416 |
var firstResolvable = -1; |
| 332 |
417 |
for (i in 0...alts.length) { |
|
|
@@ -337,7 +422,7 @@ class Main { |
| 337 |
422 |
resolvable = false; |
| 338 |
423 |
break; |
| 339 |
424 |
} |
| 340 |
|
- if (vdb.installed(at.cat, at.pkg).length == 0) installed = false; |
|
425 |
+ if (!installedSatisfies(at)) installed = false; |
| 341 |
426 |
} |
| 342 |
427 |
if (!resolvable) continue; |
| 343 |
428 |
if (installed) return i; |
|
|
@@ -347,19 +432,50 @@ class Main { |
| 347 |
432 |
} |
| 348 |
433 |
|
| 349 |
434 |
static function status(vdb: VDB, e: Entry): Status { |
| 350 |
|
- var inst = vdb.installed(e.cat, e.pkg); |
| 351 |
|
- if (inst.length == 0) return {tag: "N", old: null}; |
|
435 |
+ var exactInst = [for (i in vdb.installed(e.cat, e.pkg)) |
|
436 |
+ if (Slots.installedSlot(vdb.root, e.cat, e.pkg + "-" + i.ver.raw).slot == e.slot) i]; |
|
437 |
+ |
|
438 |
+ if (exactInst.length > 0) { |
|
439 |
+ var top = exactInst[0]; |
|
440 |
+ for (i in exactInst) { |
|
441 |
+ if (i.ver.compare(top.ver) > 0) top = i; |
|
442 |
+ } |
| 352 |
443 |
|
| 353 |
|
- var top = inst[0]; |
| 354 |
|
- for (i in inst) { |
| 355 |
|
- if (i.ver.compare(top.ver) > 0) top = i; |
|
444 |
+ var cmp = e.ver.compare(top.ver); |
|
445 |
+ var oldStr = top.ver.raw + "::" + top.repo; |
|
446 |
+ if (cmp == 0) return {tag: "R", old: null}; |
|
447 |
+ if (cmp > 0) return {tag: "U", old: oldStr}; |
|
448 |
+ return {tag: "UD", old: oldStr}; |
| 356 |
449 |
} |
| 357 |
450 |
|
| 358 |
|
- var cmp = e.ver.compare(top.ver); |
| 359 |
|
- var oldStr = top.ver.raw + "::" + top.repo; |
| 360 |
|
- if (cmp == 0) return {tag: "R", old: null}; |
| 361 |
|
- if (cmp > 0) return {tag: "U", old: oldStr}; |
| 362 |
|
- return {tag: "UD", old: oldStr}; |
|
451 |
+ var allInst = vdb.installed(e.cat, e.pkg); |
|
452 |
+ if (allInst.length == 0) return {tag: "N", old: null}; |
|
453 |
+ |
|
454 |
+ var topAll = allInst[0]; |
|
455 |
+ for (i in allInst) { |
|
456 |
+ if (i.ver.compare(topAll.ver) > 0) topAll = i; |
|
457 |
+ } |
|
458 |
+ return {tag: "NS", old: topAll.ver.raw}; |
|
459 |
+ } |
|
460 |
+ |
|
461 |
+ static function isTesting(e: Entry): Bool { |
|
462 |
+ var accept = Keywords.active(); |
|
463 |
+ var kw = ""; |
|
464 |
+ for (r in repos.list) { |
|
465 |
+ if (r.name == e.repo) { |
|
466 |
+ kw = r.tree.variable(e.path, "KEYWORDS"); |
|
467 |
+ break; |
|
468 |
+ } |
|
469 |
+ } |
|
470 |
+ |
|
471 |
+ var testing = false; |
|
472 |
+ for (t in ~/[ \t\r\n]+/g.split(kw)) { |
|
473 |
+ if (t == "") continue; |
|
474 |
+ if (accept.indexOf(t) < 0) continue; |
|
475 |
+ if (StringTools.startsWith(t, "~")) testing = true; |
|
476 |
+ else return false; |
|
477 |
+ } |
|
478 |
+ return testing; |
| 363 |
479 |
} |
| 364 |
480 |
|
| 365 |
481 |
static function showPlan(vdb: VDB, verbose: Bool) { |
|
|
@@ -377,14 +493,14 @@ class Main { |
| 377 |
493 |
counts.set(st.tag, (counts.exists(st.tag) ? counts.get(st.tag) : 0) + 1); |
| 378 |
494 |
|
| 379 |
495 |
var tagCol = switch st.tag { |
| 380 |
|
- case "N": "32"; |
|
496 |
+ case "N" | "NS": "32"; |
| 381 |
497 |
case "U": "34"; |
| 382 |
498 |
case "UD": "35"; |
| 383 |
499 |
default: "33"; |
| 384 |
500 |
}; |
| 385 |
501 |
|
| 386 |
502 |
var line = c("1;32", "[ebuild ") |
| 387 |
|
- + c(tagCol, " " + StringTools.rpad(st.tag, " ", 4) + " ") |
|
503 |
+ + c(tagCol, " " + StringTools.rpad(st.tag, " ", 2) + (isTesting(e) ? "+" : " ") + " ") |
| 388 |
504 |
+ c("1;32", "]") |
| 389 |
505 |
+ " " + c("1;37", e.cat + "/" + e.pkg + "-" + e.ver.raw) |
| 390 |
506 |
+ (showRepo ? c("0;36", "::" + e.repo) : ""); |
|
|
@@ -392,18 +508,39 @@ class Main { |
| 392 |
508 |
if (st.old != null) { |
| 393 |
509 |
line += " " + c("0;36", "[" + st.old + "]"); |
| 394 |
510 |
} |
|
511 |
+ |
|
512 |
+ if (verbose) { |
|
513 |
+ var pkgSize = Std.int(getDownloadSize(e) / 1024); |
|
514 |
+ if (pkgSize > 0) { |
|
515 |
+ line += " " + formatNum(pkgSize) + " KiB"; |
|
516 |
+ } else { |
|
517 |
+ line += " 0 KiB"; |
|
518 |
+ } |
|
519 |
+ } |
|
520 |
+ |
| 395 |
521 |
Sys.println(line); |
| 396 |
522 |
} |
| 397 |
523 |
|
| 398 |
524 |
Sys.println(""); |
| 399 |
525 |
var parts: Array<String> = []; |
| 400 |
|
- if (counts.exists("U")) parts.push(counts.get("U") + " upgrade"); |
| 401 |
|
- if (counts.exists("UD")) parts.push(counts.get("UD") + " downgrade"); |
|
526 |
+ if (counts.exists("U")) parts.push(counts.get("U") + " upgrade" + (counts.get("U") == 1 ? "" : "s")); |
|
527 |
+ if (counts.exists("UD")) parts.push(counts.get("UD") + " downgrade" + (counts.get("UD") == 1 ? "" : "s")); |
| 402 |
528 |
if (counts.exists("N")) parts.push(counts.get("N") + " new"); |
|
529 |
+ if (counts.exists("NS")) parts.push(counts.get("NS") + " in new slot"); |
| 403 |
530 |
if (counts.exists("R")) parts.push(counts.get("R") + " reinstall"); |
| 404 |
531 |
|
|
532 |
+ var totalSize = 0; |
|
533 |
+ for (e in plan) { |
|
534 |
+ totalSize += getDownloadSize(e); |
|
535 |
+ } |
|
536 |
+ var sizeKiB = Std.int(totalSize / 1024); |
|
537 |
+ |
| 405 |
538 |
var plural = plan.length == 1 ? "" : "s"; |
| 406 |
|
- Sys.println("Total: " + plan.length + " package" + plural + " (" + parts.join(", ") + ")"); |
|
539 |
+ var summary = "Total: " + plan.length + " package" + plural + " (" + parts.join(", ") + ")"; |
|
540 |
+ if (sizeKiB > 0) { |
|
541 |
+ summary += ", Size of downloads: " + formatNum(sizeKiB) + " KiB"; |
|
542 |
+ } |
|
543 |
+ if (verbose) Sys.println(summary); |
| 407 |
544 |
} |
| 408 |
545 |
|
| 409 |
546 |
static function explain(spec: String): Void { |
|
|
@@ -485,6 +622,50 @@ class Main { |
| 485 |
622 |
return out; |
| 486 |
623 |
} |
| 487 |
624 |
|
|
625 |
+ static function getDownloadSize(e: Entry): Int { |
|
626 |
+ var dir = haxe.io.Path.directory(e.path); |
|
627 |
+ var manifestPath = dir + "/Manifest"; |
|
628 |
+ if (!FileSystem.exists(manifestPath)) return 0; |
|
629 |
+ |
|
630 |
+ var baseVer = e.ver.raw; |
|
631 |
+ var dashR = baseVer.lastIndexOf("-r"); |
|
632 |
+ if (dashR > 0) { |
|
633 |
+ baseVer = baseVer.substr(0, dashR); |
|
634 |
+ } |
|
635 |
+ |
|
636 |
+ var totalSize = 0; |
|
637 |
+ try { |
|
638 |
+ var content = File.getContent(manifestPath); |
|
639 |
+ for (line in content.split("\n")) { |
|
640 |
+ var trimmed = StringTools.trim(line); |
|
641 |
+ if (StringTools.startsWith(trimmed, "DIST ")) { |
|
642 |
+ var parts = ~/[ \t]+/g.split(trimmed); |
|
643 |
+ if (parts.length >= 3) { |
|
644 |
+ var filename = parts[1]; |
|
645 |
+ if (StringTools.contains(filename, baseVer)) { |
|
646 |
+ var sz = Std.parseInt(parts[2]); |
|
647 |
+ if (sz != null) { |
|
648 |
+ totalSize += sz; |
|
649 |
+ } |
|
650 |
+ } |
|
651 |
+ } |
|
652 |
+ } |
|
653 |
+ } |
|
654 |
+ } catch (err: Dynamic) {} |
|
655 |
+ return totalSize; |
|
656 |
+ } |
|
657 |
+ |
|
658 |
+ static function formatNum(n: Int): String { |
|
659 |
+ var s = Std.string(n); |
|
660 |
+ var out = ""; |
|
661 |
+ var len = s.length; |
|
662 |
+ for (i in 0...len) { |
|
663 |
+ if (i > 0 && (len - i) % 3 == 0) out += ","; |
|
664 |
+ out += s.charAt(i); |
|
665 |
+ } |
|
666 |
+ return out; |
|
667 |
+ } |
|
668 |
+ |
| 488 |
669 |
static function mergeOne(e: Entry, count: Int, total: Int): Bool { |
| 489 |
670 |
var pos = "(" + count + " of " + total + ") "; |
| 490 |
671 |
|
|
|
@@ -631,9 +812,13 @@ class Main { |
| 631 |
812 |
} |
| 632 |
813 |
} |
| 633 |
814 |
|
|
815 |
+ rebuildSubslotDependents(); |
|
816 |
+ |
| 634 |
817 |
finishProgress(); |
| 635 |
818 |
Sys.println(""); |
| 636 |
819 |
|
|
820 |
+ if (!checkBlockers()) Sys.exit(1); |
|
821 |
+ |
| 637 |
822 |
showPlan(vdb, verbose); |
| 638 |
823 |
|
| 639 |
824 |
if (pretend || plan.length == 0) return; |
|
|
@@ -659,4 +844,4 @@ class Main { |
| 659 |
844 |
if (!mergeOne(plan[n], n + 1, total)) Sys.exit(1); |
| 660 |
845 |
} |
| 661 |
846 |
} |
| 662 |
|
-} |
|
847 |
+} |
|
|
\ No newline at end of file |