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:
23 self
.roots
: Set
[str] = set()
24 self
.non_roots
: Set
[str] = set()
25 self
.deps
: Dict
[str, List
[str]] = {}
27 def startElement(self
, name
: str, attrs
: Any
) -> None:
29 source
= attrs
.getValue("source")
30 target
= attrs
.getValue("target")
31 self
.non_roots
.add(target
)
32 self
.deps
.setdefault(source
, []).append(target
)
33 if target
in self
.roots
:
34 self
.roots
.remove(target
)
35 if source
not in self
.non_roots
:
36 self
.roots
.add(source
)
39 def getDeps(drv
: str) -> ParseNixStoreQueryGraphML
:
40 with log("Loading dependency tree..."):
41 process
= subprocess
.Popen(
42 ["nix-store", "--query", "--graphml", drv
], stdout
=subprocess
.PIPE
)
43 parser
= ParseNixStoreQueryGraphML()
45 xml
.sax
.parse(process
.stdout
, parser
)
46 assert process
.wait() == 0
50 def getDrvInfo(drv
: str) -> Any
:
51 with log("Loading %s..." % drv
):
52 process
= subprocess
.Popen(["nix",
53 "--experimental-features",
57 stdout
=subprocess
.PIPE
)
59 info
= json
.load(process
.stdout
)
60 assert process
.wait() == 0
64 def allBuilt(drv
: str) -> bool:
65 # TODO: How to pin outputs one at a time?
66 # Currently, we only pin when all outputs are available.
67 # It would be better to pin the outputs we've got.
68 return all(os
.path
.exists(o
['path']) for d
in getDrvInfo(
69 drv
).values() for o
in d
['outputs'].values())
72 def isDrv(thing
: str) -> bool:
73 return thing
.endswith(".drv")
76 def removesuffix(s
: str, suf
: str) -> str:
77 return s
[:-len(suf
)] if s
.endswith(suf
) else s
80 def pin(drv
: str) -> None:
81 outPath
= os
.path
.join(sys
.argv
[2], removesuffix(drv
, ".drv"))
82 if not os
.path
.exists(outPath
):
83 process
= subprocess
.run(["nix-store",
91 def pinBuiltThings(thing
: str,
93 deps
: Dict
[str, List
[str]]) -> None:
97 if not isDrv(thing
) or allBuilt(thing
):
100 for dep
in deps
.get(thing
, []):
101 pinBuiltThings(dep
, done
, deps
)
104 dep_graph
= getDeps(sys
.argv
[1])
105 for root
in dep_graph
.roots
:
106 pinBuiltThings(root
, set(), dep_graph
.deps
)