185 lines
6.1 KiB
Nim
185 lines
6.1 KiB
Nim
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
# SPDX-License-Identifier: Unlicense
|
|
|
|
import std/[json, os, osproc, strutils, tables]
|
|
import eris/memory_stores
|
|
import preserves, preserves/jsonhooks
|
|
import syndicate
|
|
from syndicate/protocols/dataspace import Observe
|
|
import ./nix_actor/[clients, daemons]
|
|
import ./nix_actor/libnix/[libexpr, main, stdpuspus, store]
|
|
import ./nix_actor/protocol
|
|
|
|
var nixVersion {.importcpp: "nix::nixVersion", header: "globals.hh".}: StdString
|
|
|
|
type
|
|
Value = Preserve[void]
|
|
Observe = dataspace.Observe[Cap]
|
|
|
|
proc toPreserve(state: EvalState; val: libexpr.ValueObj | libExpr.ValuePtr; E = void): Preserve[E] {.gcsafe.} =
|
|
## Convert a Nix value to a Preserves value.
|
|
# See nix::printValueAsJSON
|
|
case val.kind
|
|
of nInt:
|
|
result = val.integer.toPreserve(E)
|
|
of nFloat:
|
|
result = val.fpoint.toPreserve(E)
|
|
of nBool:
|
|
result = val.boolean.toPreserve(E)
|
|
of nString:
|
|
result = val.shallowString.toPreserve(E)
|
|
of nPath:
|
|
result = toSymbol($val.path, E)
|
|
of nNull:
|
|
result = initRecord[E]("null")
|
|
of nAttrs:
|
|
result = initDictionary(E)
|
|
for sym, attr in val.pairs:
|
|
let key = symbolString(state, sym).toSymbol(E)
|
|
# Nix string to Nim string to Preserves symbol
|
|
result[key] = state.toPreserve(attr, E)
|
|
of nList:
|
|
result = initSequence(0, E)
|
|
for e in val.items:
|
|
result.sequence.add(state.toPreserve(e, E))
|
|
else:
|
|
raise newException(ValueError, "cannot preserve " & $val.kind)
|
|
|
|
proc eval(state: EvalState; code: string): Value =
|
|
## Evaluate Nix `code` to a Preserves value.
|
|
var nixVal: libexpr.ValueObj
|
|
let expr = state.parseExprFromString(code, getCurrentDir())
|
|
state.eval(expr, nixVal)
|
|
state.forceValueDeep(nixVal)
|
|
state.toPreserve(nixVal, void)
|
|
|
|
proc parseArgs(args: var seq[string]; opts: AttrSet) =
|
|
for sym, val in opts:
|
|
add(args, "--" & $sym)
|
|
if not val.isString "":
|
|
var js: JsonNode
|
|
if fromPreserve(js, val): add(args, $js)
|
|
else: stderr.writeLine "invalid option --", sym, " ", val
|
|
|
|
#[
|
|
proc parseNarinfo(info: var AttrSet; text: string) =
|
|
var
|
|
key, val: string
|
|
off: int
|
|
while off < len(text):
|
|
off = off + parseUntil(text, key, ':', off) + 1
|
|
off = off + skipWhitespace(text, off)
|
|
off = off + parseUntil(text, val, '\n', off) + 1
|
|
if key != "" and val != "":
|
|
if allCharsInSet(val, Digits):
|
|
info[Symbol key] = val.parsePreserves
|
|
else:
|
|
info[Symbol key] = val.toPreserve
|
|
|
|
proc narinfo(turn: var Turn; ds: Cap; path: string) =
|
|
let
|
|
client = newAsyncHttpClient()
|
|
url = "https://cache.nixos.org/" & path & ".narinfo"
|
|
futGet = get(client, url)
|
|
addCallback(futGet, turn) do (turn: var Turn):
|
|
let resp = read(futGet)
|
|
if code(resp) != Http200:
|
|
close(client)
|
|
else:
|
|
let futBody = body(resp)
|
|
addCallback(futBody, turn) do (turn: var Turn):
|
|
close(client)
|
|
var narinfo = Narinfo(path: path)
|
|
parseNarinfo(narinfo.info, read(futBody))
|
|
discard publish(turn, ds, narinfo)
|
|
]# # I never link to openssl if I can avoid it.
|
|
|
|
proc build(spec: string): Build =
|
|
var execOutput = execProcess("nix", args = ["build", "--json", "--no-link", spec], options = {poUsePath})
|
|
var js = parseJson(execOutput)
|
|
Build(input: spec, output: js[0].toPreserve)
|
|
|
|
proc realise(realise: Realise): seq[string] =
|
|
var execlines = execProcess("nix-store", args = ["--realize", realise.drv], options = {poUsePath})
|
|
split(strip(execlines), '\n')
|
|
|
|
proc instantiate(instantiate: Instantiate): Value =
|
|
const cmd = "nix-instantiate"
|
|
var args = @["--expr", instantiate.expr]
|
|
parseArgs(args, instantiate.options)
|
|
var execOutput = strip execProcess(cmd, args = args, options = {poUsePath})
|
|
execOutput.toPreserve
|
|
|
|
proc bootNixFacet(turn: var Turn; ds: Cap): Facet =
|
|
# let store = openStore()
|
|
result = inFacet(turn) do (turn: var Turn):
|
|
|
|
during(turn, ds, ?Observe(pattern: !Build) ?? {0: grabLit()}) do (spec: string):
|
|
discard publish(turn, ds, build(spec))
|
|
|
|
during(turn, ds, ?Observe(pattern: !Realise) ?? {0: grabLit()}) do (drvPath: string):
|
|
var ass = Realise(drv: drvPath)
|
|
ass.outputs = realise(ass)
|
|
discard publish(turn, ds, ass)
|
|
|
|
during(turn, ds, ?Observe(pattern: !Instantiate) ?? {0: grabLit(), 1: grabDict()}) do (e: string, o: Value):
|
|
var ass = Instantiate(expr: e)
|
|
if not fromPreserve(ass.options, unpackLiterals(o)):
|
|
stderr.writeLine "invalid options ", o
|
|
else:
|
|
ass.result = instantiate(ass)
|
|
discard publish(turn, ds, ass)
|
|
|
|
#[
|
|
during(turn, ds, ?Observe(pattern: !Narinfo) ?? {0: grabLit()}) do (path: string):
|
|
narinfo(turn, ds, path)
|
|
]#
|
|
|
|
type
|
|
CapArgs {.preservesDictionary.} = object
|
|
dataspace: Cap
|
|
ClientSideArgs {.preservesDictionary.} = object
|
|
`listen-socket`: string
|
|
DaemonSideArgs {.preservesDictionary.} = object
|
|
`daemon-socket`: string
|
|
|
|
proc runNixActor(nixState: EvalState) =
|
|
let erisStore = newMemoryStore()
|
|
runActor("nix_actor") do (root: Cap; turn: var Turn):
|
|
connectStdio(root, turn)
|
|
|
|
let pat = ?CapArgs
|
|
during(turn, root, pat) do (ds: Cap):
|
|
|
|
discard publish(turn, ds,
|
|
initRecord("nixVersion", toPreserve($nixVersion.c_str)))
|
|
|
|
discard bootNixFacet(turn, ds)
|
|
|
|
let pat = ?Observe(pattern: !Eval) ?? {0: grabLit(), 1: grabDict()}
|
|
during(turn, ds, pat) do (e: string, o: Assertion):
|
|
var ass = Eval(expr: e)
|
|
doAssert fromPreserve(ass.options, unpackLiterals(o))
|
|
# unused options
|
|
try:
|
|
ass.result = eval(nixState, ass.expr)
|
|
discard publish(turn, ds, ass)
|
|
except CatchableError as err:
|
|
stderr.writeLine "failed to evaluate ", ass.expr, ": ", err.msg
|
|
except StdException as err:
|
|
stderr.writeLine "failed to evaluate ", ass.expr, ": ", err.what
|
|
|
|
during(turn, root, ?ClientSideArgs) do (socketPath: string):
|
|
bootClientSide(turn, ds, erisStore, socketPath)
|
|
|
|
during(turn, root, ?DaemonSideArgs) do (socketPath: string):
|
|
bootDaemonSide(turn, ds, erisStore, socketPath)
|
|
|
|
proc main =
|
|
initNix()
|
|
initGC()
|
|
let nixStore = openStore()
|
|
runNixActor(newEvalState(nixStore))
|
|
|
|
main()
|