1 from contextlib
import contextmanager
9 from typing
import Any
, Dict
, Iterator
, List
, Set
13 def log(msg
: str) -> Iterator
[None]:
14 print(msg
, file=sys
.stderr
, end
='', flush
=True)
18 print('\r', file=sys
.stderr
, end
='', flush
=True)
21 class ParseNixStoreQueryGraphML(xml
.sax
.handler
.ContentHandler
):
22 def __init__(self
) -> None:
24 self
.roots
: Set
[str] = set()
25 self
.non_roots
: Set
[str] = set()
26 self
.deps
: Dict
[str, List
[str]] = {}
28 def startElement(self
, name
: str, attrs
: Any
) -> None:
30 source
= attrs
.getValue("source")
31 target
= attrs
.getValue("target")
32 self
.non_roots
.add(target
)
33 self
.deps
.setdefault(source
, []).append(target
)
34 if target
in self
.roots
:
35 self
.roots
.remove(target
)
36 if source
not in self
.non_roots
:
37 self
.roots
.add(source
)
40 def getDeps(drv
: str) -> ParseNixStoreQueryGraphML
:
41 with log("Loading dependency tree..."):
42 with subprocess
.Popen(
43 ["nix-store", "--query", "--graphml", drv
], stdout
=subprocess
.PIPE
) as process
:
44 parser
= ParseNixStoreQueryGraphML()
46 xml
.sax
.parse(process
.stdout
, parser
)
47 assert process
.wait() == 0
51 def getDrvInfo(drv
: str) -> Any
:
52 with log(f
"Loading {drv}..."):
53 with subprocess
.Popen(
54 ["nix", "--experimental-features", "nix-command",
55 "show-derivation", "/nix/store/" + drv
],
56 stdout
=subprocess
.PIPE
) as process
:
58 info
= json
.load(process
.stdout
)
59 assert process
.wait() == 0
63 def allBuilt(drv
: str) -> bool:
64 # TODO: How to pin outputs one at a time?
65 # Currently, we only pin when all outputs are available.
66 # It would be better to pin the outputs we've got.
67 return all(os
.path
.exists(o
['path']) for d
in getDrvInfo(
68 drv
).values() for o
in d
['outputs'].values())
71 def isDrv(thing
: str) -> bool:
72 return thing
.endswith(".drv")
75 def removesuffix(s
: str, suf
: str) -> str:
76 return s
[:-len(suf
)] if s
.endswith(suf
) else s
79 def pin(drv
: str) -> None:
80 outPath
= os
.path
.join(sys
.argv
[2], removesuffix(drv
, ".drv"))
81 if not os
.path
.exists(outPath
):
82 subprocess
.run(["nix-store", "--realise", "--add-root",
83 outPath
, "/nix/store/" + drv
], check
=True)
86 def pinBuiltThings(thing
: str,
88 deps
: Dict
[str, List
[str]]) -> None:
92 if not isDrv(thing
) or allBuilt(thing
):
95 for dep
in deps
.get(thing
, []):
96 pinBuiltThings(dep
, done
, deps
)
99 dep_graph
= getDeps(sys
.argv
[1])
100 for root
in dep_graph
.roots
:
101 pinBuiltThings(root
, set(), dep_graph
.deps
)