acooliodude / portage-hx

Push x1 Browse files
ACoolioDude committed 1d ago parent d412a4e commit 1b3efb177e126f332e805dfa15880332c0bfd125
Showing 6 changed files with 318 additions and 22 deletions. UnifiedSplit
+206 −21 src/Main.hx
@@ -8,7 +8,9 @@ typedef Entry = {
8 ver: Version, 8 ver: Version,
9 path: String, 9 path: String,
10 repo: String, 10 repo: String,
11 - deps: Array<String> 11 + slot: String,
12 + deps: Array<String>,
13 + subslot: String
12 } 14 }
13 15
14 typedef Status = { 16 typedef Status = {
@@ -195,11 +197,62 @@ class Main {
195 197
196 static function installedSatisfies(a: Atom): Bool { 198 static function installedSatisfies(a: Atom): Bool {
197 for (i in vdb.installed(a.cat, a.pkg)) { 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 if (AtomParser.matches(a, i.ver)) return true; 202 if (AtomParser.matches(a, i.ver)) return true;
199 } 203 }
200 return false; 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 static function hostSatisfies(a: Atom): Bool { 256 static function hostSatisfies(a: Atom): Bool {
204 for (i in hostVdb.installed(a.cat, a.pkg)) { 257 for (i in hostVdb.installed(a.cat, a.pkg)) {
205 if (AtomParser.matches(a, i.ver)) return true; 258 if (AtomParser.matches(a, i.ver)) return true;
@@ -236,7 +289,7 @@ class Main {
236 289
237 if (hit == null || topInst.ver.compare(hit.ver) >= 0) { 290 if (hit == null || topInst.ver.compare(hit.ver) >= 0) {
238 if (AtomParser.matches(a, topInst.ver)) { 291 if (AtomParser.matches(a, topInst.ver)) {
239 - var matchedRepo = null; 292 + var matchedRepo: Dynamic = null;
240 for (r in repos.list) { 293 for (r in repos.list) {
241 if (r.name == topInst.repo) { 294 if (r.name == topInst.repo) {
242 matchedRepo = r; 295 matchedRepo = r;
@@ -265,7 +318,10 @@ class Main {
265 } 318 }
266 319
267 var key = a.cat + "/" + a.pkg + "-" + targetVer.raw; 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 seen.set(key, true); 325 seen.set(key, true);
270 326
271 if (chain.length > 150) { 327 if (chain.length > 150) {
@@ -304,7 +360,17 @@ class Main {
304 } 360 }
305 361
306 chain.pop(); 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 static function upgradeAvailable(a: Atom): Bool { 376 static function upgradeAvailable(a: Atom): Bool {
@@ -327,6 +393,25 @@ class Main {
327 return true; 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 static function chooser(alts: Array<Array<Atom>>): Int { 415 static function chooser(alts: Array<Array<Atom>>): Int {
331 var firstResolvable = -1; 416 var firstResolvable = -1;
332 for (i in 0...alts.length) { 417 for (i in 0...alts.length) {
@@ -337,7 +422,7 @@ class Main {
337 resolvable = false; 422 resolvable = false;
338 break; 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 if (!resolvable) continue; 427 if (!resolvable) continue;
343 if (installed) return i; 428 if (installed) return i;
@@ -347,19 +432,50 @@ class Main {
347 } 432 }
348 433
349 static function status(vdb: VDB, e: Entry): Status { 434 static function status(vdb: VDB, e: Entry): Status {
350 - var inst = vdb.installed(e.cat, e.pkg); 435 + var exactInst = [for (i in vdb.installed(e.cat, e.pkg))
351 - if (inst.length == 0) return {tag: "N", old: null}; 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]; 444 + var cmp = e.ver.compare(top.ver);
354 - for (i in inst) { 445 + var oldStr = top.ver.raw + "::" + top.repo;
355 - if (i.ver.compare(top.ver) > 0) top = i; 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); 451 + var allInst = vdb.installed(e.cat, e.pkg);
359 - var oldStr = top.ver.raw + "::" + top.repo; 452 + if (allInst.length == 0) return {tag: "N", old: null};
360 - if (cmp == 0) return {tag: "R", old: null}; 453 +
361 - if (cmp > 0) return {tag: "U", old: oldStr}; 454 + var topAll = allInst[0];
362 - return {tag: "UD", old: oldStr}; 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 static function showPlan(vdb: VDB, verbose: Bool) { 481 static function showPlan(vdb: VDB, verbose: Bool) {
@@ -377,14 +493,14 @@ class Main {
377 counts.set(st.tag, (counts.exists(st.tag) ? counts.get(st.tag) : 0) + 1); 493 counts.set(st.tag, (counts.exists(st.tag) ? counts.get(st.tag) : 0) + 1);
378 494
379 var tagCol = switch st.tag { 495 var tagCol = switch st.tag {
380 - case "N": "32"; 496 + case "N" | "NS": "32";
381 case "U": "34"; 497 case "U": "34";
382 case "UD": "35"; 498 case "UD": "35";
383 default: "33"; 499 default: "33";
384 }; 500 };
385 501
386 var line = c("1;32", "[ebuild ") 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 + c("1;32", "]") 504 + c("1;32", "]")
389 + " " + c("1;37", e.cat + "/" + e.pkg + "-" + e.ver.raw) 505 + " " + c("1;37", e.cat + "/" + e.pkg + "-" + e.ver.raw)
390 + (showRepo ? c("0;36", "::" + e.repo) : ""); 506 + (showRepo ? c("0;36", "::" + e.repo) : "");
@@ -392,18 +508,39 @@ class Main {
392 if (st.old != null) { 508 if (st.old != null) {
393 line += " " + c("0;36", "[" + st.old + "]"); 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 Sys.println(line); 521 Sys.println(line);
396 } 522 }
397 523
398 Sys.println(""); 524 Sys.println("");
399 var parts: Array<String> = []; 525 var parts: Array<String> = [];
400 - if (counts.exists("U")) parts.push(counts.get("U") + " upgrade"); 526 + if (counts.exists("U")) parts.push(counts.get("U") + " upgrade" + (counts.get("U") == 1 ? "" : "s"));
401 - if (counts.exists("UD")) parts.push(counts.get("UD") + " downgrade"); 527 + if (counts.exists("UD")) parts.push(counts.get("UD") + " downgrade" + (counts.get("UD") == 1 ? "" : "s"));
402 if (counts.exists("N")) parts.push(counts.get("N") + " new"); 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 if (counts.exists("R")) parts.push(counts.get("R") + " reinstall"); 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 var plural = plan.length == 1 ? "" : "s"; 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 static function explain(spec: String): Void { 546 static function explain(spec: String): Void {
@@ -485,6 +622,50 @@ class Main {
485 return out; 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 static function mergeOne(e: Entry, count: Int, total: Int): Bool { 669 static function mergeOne(e: Entry, count: Int, total: Int): Bool {
489 var pos = "(" + count + " of " + total + ") "; 670 var pos = "(" + count + " of " + total + ") ";
490 671
@@ -631,9 +812,13 @@ class Main {
631 } 812 }
632 } 813 }
633 814
815 + rebuildSubslotDependents();
816 +
634 finishProgress(); 817 finishProgress();
635 Sys.println(""); 818 Sys.println("");
636 819
820 + if (!checkBlockers()) Sys.exit(1);
821 +
637 showPlan(vdb, verbose); 822 showPlan(vdb, verbose);
638 823
639 if (pretend || plan.length == 0) return; 824 if (pretend || plan.length == 0) return;
@@ -659,4 +844,4 @@ class Main {
659 if (!mergeOne(plan[n], n + 1, total)) Sys.exit(1); 844 if (!mergeOne(plan[n], n + 1, total)) Sys.exit(1);
660 } 845 }
661 } 846 }
662 -} 847 +}
\ No newline at end of file \ No newline at end of file
+1 −0 src/import.hx
@@ -5,6 +5,7 @@ import portage.Keywords;
5 import portage.Mask; 5 import portage.Mask;
6 import portage.Repository; 6 import portage.Repository;
7 import portage.Search; 7 import portage.Search;
8 +import portage.Slots;
8 import portage.Sync; 9 import portage.Sync;
9 import portage.Tree; 10 import portage.Tree;
10 import portage.UseFlags; 11 import portage.UseFlags;
+18 −0 src/portage/Atom.hx
@@ -46,6 +46,24 @@ class AtomParser {
46 }; 46 };
47 } 47 }
48 48
49 + public static function slotMatches(a: Atom, slot: String, subslot: String): Bool {
50 + if (a.slot == null || a.slot == "*") return true;
51 + var req = a.slot;
52 + if (StringTools.endsWith(req, "=")) {
53 + req = req.substr(0, req.length - 1);
54 + }
55 + var slash = req.indexOf("/");
56 + if (slash >= 0) {
57 + var rSlot = req.substr(0, slash);
58 + var rSub = req.substr(slash + 1);
59 + if (rSlot != "" && rSlot != slot) return false;
60 + if (rSub != "" && rSub != subslot) return false;
61 + return true;
62 + }
63 + if (req != "" && req != slot) return false;
64 + return true;
65 + }
66 +
49 public static function matches(a: Atom, v: Version): Bool { 67 public static function matches(a: Atom, v: Version): Bool {
50 if (a.ver == null) return true; 68 if (a.ver == null) return true;
51 var target = a.ver; 69 var target = a.ver;
+28 −1 src/portage/Dependencies.hx
@@ -5,6 +5,7 @@ enum Dep {
5 AllOf(kids: Array<Dep>); 5 AllOf(kids: Array<Dep>);
6 AnyOf(alts: Array<Dep>); 6 AnyOf(alts: Array<Dep>);
7 Cond(flag: String, negate: Bool, kids: Array<Dep>); 7 Cond(flag: String, negate: Bool, kids: Array<Dep>);
8 + Block(a: Atom, strong: Bool);
8 } 9 }
9 10
10 typedef Chooser = Array<Array<Atom>> -> Int; 11 typedef Chooser = Array<Array<Atom>> -> Int;
@@ -49,7 +50,9 @@ class Dependencies {
49 var flag = t.substr(start, t.length - start - 1); 50 var flag = t.substr(start, t.length - start - 1);
50 out.push(Cond(flag, neg, group())); 51 out.push(Cond(flag, neg, group()));
51 } else if (StringTools.startsWith(t, "!")) { 52 } else if (StringTools.startsWith(t, "!")) {
52 - 53 + var strong = StringTools.startsWith(t, "!!");
54 + var blocked = AtomParser.parse(t.substr(strong ? 2 : 1));
55 + if (blocked != null) out.push(Block(blocked, strong));
53 } else { 56 } else {
54 var a = AtomParser.parse(t); 57 var a = AtomParser.parse(t);
55 if (a == null) { 58 if (a == null) {
@@ -70,6 +73,28 @@ class Dependencies {
70 throw 'expected "(" in dependency string'; 73 throw 'expected "(" in dependency string';
71 } 74 }
72 75
76 + public static function blockers(deps: Array<Dep>, use: Map<String, Bool>): Array<{atom: Atom, strong: Bool}> {
77 + var out: Array<{atom: Atom, strong: Bool}> = [];
78 + for (d in deps) collectBlockers(d, use, out);
79 + return out;
80 + }
81 +
82 + static function collectBlockers(d: Dep, use: Map<String, Bool>, out: Array<{atom: Atom, strong: Bool}>): Void {
83 + switch d {
84 + case Leaf(_):
85 +
86 + case Block(a, strong):
87 + out.push({atom: a, strong: strong});
88 + case AllOf(kids):
89 + for (k in kids) collectBlockers(k, use, out);
90 + case AnyOf(alts):
91 + for (alt in alts) collectBlockers(alt, use, out);
92 + case Cond(flag, negate, kids):
93 + var on = use.get(flag) == true;
94 + if (on != negate) for (k in kids) collectBlockers(k, use, out);
95 + }
96 + }
97 +
73 public static function eval(deps: Array<Dep>, use: Map<String, Bool>, pick: Chooser): Array<Atom> { 98 public static function eval(deps: Array<Dep>, use: Map<String, Bool>, pick: Chooser): Array<Atom> {
74 var out: Array<Atom> = []; 99 var out: Array<Atom> = [];
75 for (d in deps) evalOne(d, use, pick, out); 100 for (d in deps) evalOne(d, use, pick, out);
@@ -82,6 +107,8 @@ class Dependencies {
82 out.push(a); 107 out.push(a);
83 case AllOf(kids): 108 case AllOf(kids):
84 for (k in kids) evalOne(k, use, pick, out); 109 for (k in kids) evalOne(k, use, pick, out);
110 + case Block(_, _):
111 +
85 case Cond(flag, negate, kids): 112 case Cond(flag, negate, kids):
86 var on = use.get(flag) == true; 113 var on = use.get(flag) == true;
87 if (on != negate) for (k in kids) evalOne(k, use, pick, out); 114 if (on != negate) for (k in kids) evalOne(k, use, pick, out);
+56 −0 src/portage/Slots.hx new file mode 100644
@@ -0,0 +1,56 @@
1 +package portage;
2 +
3 +import sys.FileSystem;
4 +import sys.io.File;
5 +
6 +class Slots {
7 + public static function installedSlot(root: String, cat: String, pkgVer: String): {slot: String, subslot: String} {
8 + var raw = read(root + "/var/db/pkg/" + cat + "/" + pkgVer + "/SLOT");
9 + if (raw == "") return {slot: "", subslot: ""};
10 +
11 + var parts = raw.split("/");
12 + var slot = parts[0] == "" ? "0" : parts[0];
13 + var subslot = (parts.length > 1 && parts[1] != "") ? parts[1] : slot;
14 + return {slot: slot, subslot: subslot};
15 + }
16 +
17 + public static function dependentsWithSubslotOp(root: String, cat: String, pkg: String): Array<String> {
18 + var label = cat + "/" + pkg;
19 + var out: Array<String> = [];
20 + var base = root + "/var/db/pkg";
21 + if (!FileSystem.exists(base)) return out;
22 +
23 + for (catName in FileSystem.readDirectory(base)) {
24 + var catDir = base + "/" + catName;
25 + if (!FileSystem.isDirectory(catDir)) continue;
26 +
27 + for (pkgVer in FileSystem.readDirectory(catDir)) {
28 + var dir = catDir + "/" + pkgVer;
29 + if (!FileSystem.isDirectory(dir)) continue;
30 +
31 + var name = read(dir + "/PN");
32 + if (name == "") continue;
33 + var depLabel = catName + "/" + name;
34 + if (depLabel == label) continue;
35 +
36 + if (usesSubslotOp(dir + "/DEPEND", label) || usesSubslotOp(dir + "/RDEPEND", label)) {
37 + if (out.indexOf(depLabel) < 0) out.push(depLabel);
38 + }
39 + }
40 + }
41 + return out;
42 + }
43 +
44 + static function usesSubslotOp(file: String, label: String): Bool {
45 + if (!FileSystem.exists(file)) return false;
46 + for (t in ~/[ \t\r\n]+/g.split(File.getContent(file))) {
47 + if (StringTools.startsWith(t, label + ":") && StringTools.endsWith(t, "=")) return true;
48 + }
49 + return false;
50 + }
51 +
52 + static function read(file: String): String {
53 + if (!FileSystem.exists(file)) return "";
54 + return StringTools.trim(File.getContent(file));
55 + }
56 +}
+9 −0 src/portage/Tree.hx
@@ -111,6 +111,15 @@ class Tree {
111 }); 111 });
112 } 112 }
113 113
114 + public function slotInfo(path: String): {slot: String, subslot: String} {
115 + var raw = variable(path, "SLOT");
116 + if (raw == "") raw = "0";
117 + var parts = raw.split("/");
118 + var slot = parts[0];
119 + var subslot = parts.length > 1 ? parts[1] : slot;
120 + return {slot: slot, subslot: subslot};
121 + }
122 +
114 public function depSpec(path: String): String { 123 public function depSpec(path: String): String {
115 return variable(path, "DEPEND") + " " + variable(path, "RDEPEND") + " " + variable(path, "BDEPEND"); 124 return variable(path, "DEPEND") + " " + variable(path, "RDEPEND") + " " + variable(path, "BDEPEND");
116 } 125 }