Native evaluation (rather than by subprocess)
This commit is contained in:
parent
a6aeaadf81
commit
d51d9b3c34
|
@ -1,4 +1,4 @@
|
|||
version = "20230612"
|
||||
version = "20230613"
|
||||
author = "Emery Hemingway"
|
||||
description = "Syndicated Nix Actor"
|
||||
license = "Unlicense"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
include_rules
|
||||
: nix_actor.nim | $(SYNDICATE_PROTOCOL) ./<protocol> |> !nim_bin |> {bin}
|
||||
: {bin} |> !assert_built |>
|
||||
: foreach *.nim | $(SYNDICATE_PROTOCOL) ./<protocol> |> !nim_bin |> {bin}
|
||||
: foreach {bin} |> !assert_built |>
|
||||
|
|
|
@ -1,19 +1,57 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[json, osproc, parseutils, strutils, tables]
|
||||
import std/[json, os, osproc, parseutils, strutils, tables]
|
||||
import eris/memory_stores
|
||||
import preserves, preserves/jsonhooks
|
||||
import syndicate
|
||||
from syndicate/protocols/dataspace import Observe
|
||||
import ./nix_actor/protocol
|
||||
import ./nix_actor/[clients, daemons]
|
||||
|
||||
import ./nix_actor/libnix/[libexpr, main, store]
|
||||
import ./nix_actor/protocol
|
||||
|
||||
type
|
||||
Value = Preserve[void]
|
||||
Observe = dataspace.Observe[Ref]
|
||||
|
||||
proc toPreserve(val: libexpr.ValueObj | libExpr.ValuePtr; state: EvalState; E = void): Preserve[E] {.gcsafe.} =
|
||||
## Convert a Nix value to a Preserves value.
|
||||
case val.kind
|
||||
of nThunk:
|
||||
result = initRecord[E]("thunk")
|
||||
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 key, val in val.pairs:
|
||||
result[symbolString(state, key).toSymbol(E)] = val.toPreserve(state, E)
|
||||
of nList:
|
||||
result = initSequence(0, E)
|
||||
for e in val.items:
|
||||
result.sequence.add(e.toPreserve(state, E))
|
||||
of nFunction:
|
||||
result = initRecord[E]("func")
|
||||
of nExternal:
|
||||
result = initRecord[E]("external")
|
||||
|
||||
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)
|
||||
nixVal.toPreserve(state, void)
|
||||
|
||||
proc parseArgs(args: var seq[string]; opts: AttrSet) =
|
||||
for sym, val in opts:
|
||||
add(args, "--" & $sym)
|
||||
|
@ -71,15 +109,6 @@ proc instantiate(instantiate: Instantiate): Value =
|
|||
var execOutput = strip execProcess(cmd, args = args, options = {poUsePath})
|
||||
execOutput.toPreserve
|
||||
|
||||
proc eval(eval: Eval): Value =
|
||||
const cmd = "nix"
|
||||
var args = @["eval", "--expr", eval.expr]
|
||||
parseArgs(args, eval.options)
|
||||
var execOutput = strip execProcess(cmd, args = args, options = {poUsePath})
|
||||
if execOutput != "":
|
||||
var js = parseJson(execOutput)
|
||||
result = js.toPreserve
|
||||
|
||||
proc bootNixFacet(turn: var Turn; ds: Ref): Facet =
|
||||
# let store = openStore()
|
||||
result = inFacet(turn) do (turn: var Turn):
|
||||
|
@ -100,14 +129,6 @@ proc bootNixFacet(turn: var Turn; ds: Ref): Facet =
|
|||
ass.result = instantiate(ass)
|
||||
discard publish(turn, ds, ass)
|
||||
|
||||
during(turn, ds, ?Observe(pattern: !Eval) ?? {0: grabLit(), 1: grabDict()}) do (e: string, o: Value):
|
||||
var ass = Eval(expr: e)
|
||||
if not fromPreserve(ass.options, unpackLiterals(o)):
|
||||
stderr.writeLine "invalid options ", o
|
||||
else:
|
||||
ass.result = eval(ass)
|
||||
discard publish(turn, ds, ass)
|
||||
|
||||
#[
|
||||
during(turn, ds, ?Observe(pattern: !Narinfo) ?? {0: grabLit()}) do (path: string):
|
||||
narinfo(turn, ds, path)
|
||||
|
@ -121,15 +142,28 @@ type
|
|||
DaemonSideArgs {.preservesDictionary.} = object
|
||||
`daemon-socket`: string
|
||||
|
||||
main.initNix()
|
||||
libexpr.initGC()
|
||||
|
||||
runActor("main") do (root: Ref; turn: var Turn):
|
||||
let store = newMemoryStore()
|
||||
let
|
||||
erisStore = newMemoryStore()
|
||||
nixStore = openStore()
|
||||
nixState = newEvalState(nixStore)
|
||||
connectStdio(root, turn)
|
||||
|
||||
during(turn, root, ?RefArgs) do (ds: Ref):
|
||||
discard bootNixFacet(turn, ds)
|
||||
|
||||
during(turn, ds, ?Observe(pattern: !Eval) ?? {0: grabLit(), 1: grabDict()}) do (e: string, o: Assertion):
|
||||
var ass = Eval(expr: e)
|
||||
doAssert fromPreserve(ass.options, unpackLiterals(o))
|
||||
# unused options
|
||||
ass.result = eval(nixState, ass.expr)
|
||||
discard publish(turn, ds, ass)
|
||||
|
||||
during(turn, root, ?ClientSideArgs) do (socketPath: string):
|
||||
bootClientSide(turn, ds, store, socketPath)
|
||||
bootClientSide(turn, ds, erisStore, socketPath)
|
||||
|
||||
during(turn, root, ?DaemonSideArgs) do (socketPath: string):
|
||||
bootDaemonSide(turn, ds, store, socketPath)
|
||||
bootDaemonSide(turn, ds, erisStore, socketPath)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
backend:cpp
|
|
@ -1,7 +1,7 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[asyncdispatch, asyncnet, sets, streams, strutils]
|
||||
import std/[asyncdispatch, asyncnet, sets, strutils]
|
||||
from std/algorithm import sort
|
||||
|
||||
import eris
|
||||
|
@ -9,18 +9,9 @@ import preserves, syndicate
|
|||
from syndicate/protocols/dataspace import Observe
|
||||
import ./protocol, ./sockets
|
||||
|
||||
type Value = Preserve[void]
|
||||
|
||||
proc merge(items: varargs[Value]): Value =
|
||||
# TODO: just a hack, not a proper imlementation
|
||||
# https://preserves.dev/preserves.html#appendix-merging-values
|
||||
result = initDictionary()
|
||||
for e in items:
|
||||
for (key, val) in e.pairs:
|
||||
result[key] = val
|
||||
cannonicalize(result)
|
||||
|
||||
type Observe = dataspace.Observe[Ref]
|
||||
type
|
||||
Value = Preserve[void]
|
||||
Observe = dataspace.Observe[Ref]
|
||||
|
||||
proc recvError(daemon: Session): Future[string] {.async.} =
|
||||
discard #[typ]# await recvString(daemon)
|
||||
|
@ -137,9 +128,7 @@ proc recvLegacyPathAttrs(daemon: Session): Future[AddToStoreAttrs] {.async.} =
|
|||
return info
|
||||
|
||||
proc addToStore(daemon: Session; store: ErisStore; request: AddToStoreClientAttrs): Future[(string, AddToStoreAttrs)] {.async.} =
|
||||
let
|
||||
erisCap = parseCap(request.eris)
|
||||
stream = newErisStream(store, erisCap)
|
||||
let erisCap = parseCap(request.eris)
|
||||
await send(daemon, Word wopAddToStore)
|
||||
await send(daemon, request.name)
|
||||
await send(daemon, string request.`ca-method`)
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import ./stdpuspus, ./store
|
||||
|
||||
{.passC: staticExec("pkg-config --cflags nix-expr").}
|
||||
{.passL: staticExec("pkg-config --libs nix-expr").}
|
||||
|
||||
proc parentDir(path: string): string =
|
||||
var i = path.high
|
||||
while path[i] != '/': dec(i)
|
||||
path[0..i]
|
||||
|
||||
{.passC: "-I" & parentDir(currentSourcePath).}
|
||||
|
||||
type
|
||||
NixInt* = int64
|
||||
NixFloat* = float64
|
||||
|
||||
ValueKind* {.importcpp: "nix::ValueType", header: "value.hh".} = enum
|
||||
nThunk,
|
||||
nInt,
|
||||
nFloat,
|
||||
nBool,
|
||||
nString,
|
||||
nPath,
|
||||
nNull,
|
||||
nAttrs,
|
||||
nList,
|
||||
nFunction,
|
||||
nExternal,
|
||||
Value* = ValueObj | ValuePtr
|
||||
ValuePtr* = ptr ValueObj
|
||||
ValueObj* {.importcpp: "nix::Value", header: "value.hh".} = object
|
||||
integer*: NixInt
|
||||
boolean*: bool
|
||||
string: StringContext
|
||||
path*: cstring
|
||||
fpoint*: NixFloat
|
||||
attrs: Bindings
|
||||
StringContext = object
|
||||
s: cstring
|
||||
Symbol* {.importcpp: "nix::Symbol", header: "symbol-table.hh".} = object
|
||||
discard
|
||||
Attr {.importcpp: "nix::Attr", header: "attr-set.hh".} = object
|
||||
name: Symbol
|
||||
value: ValuePtr
|
||||
Bindings = ptr BindginsObj
|
||||
BindginsObj {.importcpp: "nix::Bindings", header: "attr-set.hh".} = object
|
||||
discard
|
||||
|
||||
proc kind*(val: Value): ValueKind {.importcpp: "#.type()".}
|
||||
|
||||
proc shallowString*(val: Value): string =
|
||||
if val.kind != nString:
|
||||
raise newException(FieldDefect, "Value not an attribute set")
|
||||
$val.string.s
|
||||
|
||||
proc size(bindings: Bindings): csize_t {.importcpp.}
|
||||
|
||||
proc `[]`(b: Bindings; i: Natural): Attr {.importcpp: "(*#)[#]".}
|
||||
|
||||
iterator pairs*(val: Value): (Symbol, ValuePtr) =
|
||||
if val.kind != nAttrs:
|
||||
raise newException(FieldDefect, "Value not an attribute set")
|
||||
for i in 0..<val.attrs.size():
|
||||
let attr = val.attrs[i]
|
||||
yield (attr.name, attr.value)
|
||||
|
||||
proc listSize(val: Value): csize_t {.importcpp.}
|
||||
|
||||
proc listElems(val: Value): ptr UncheckedArray[ValuePtr] {.importcpp.}
|
||||
|
||||
iterator items*(val: Value): ValuePtr =
|
||||
if val.kind != nList:
|
||||
raise newException(FieldDefect, "Value not a list")
|
||||
for i in 0..<val.listSize:
|
||||
yield val.listElems()[i]
|
||||
|
||||
type
|
||||
ExprObj {.importcpp: "nix::Expr", header: "nixexpr.hh".} = object
|
||||
discard
|
||||
Expr* = ptr ExprObj
|
||||
EvalState* {.importcpp: "nix::ref<nix::EvalState>", header: "eval.hh".} = object
|
||||
discard
|
||||
|
||||
proc newEvalState*(store: Store): EvalState {.
|
||||
importcpp: "nix::newEvalState(@)", header: "seepuspus.hh".}
|
||||
|
||||
proc parseExprFromString*(state: EvalState; s, basePath: cstring): Expr {.
|
||||
importcpp: "#->parseExprFromString(@)".}
|
||||
|
||||
proc eval*(state: EvalState; expr: Expr; value: var ValueObj) {.
|
||||
importcpp: "#->eval(@)".}
|
||||
|
||||
proc forceValueDeep*(state: EvalState; value: var ValueObj) {.
|
||||
importcpp: "#->forceValueDeep(@)".}
|
||||
|
||||
proc stringView(state: EvalState; sym: Symbol): StringView {.
|
||||
importcpp: "((std::string_view)#->symbols[#])".}
|
||||
|
||||
proc symbolString*(state: EvalState; sym: Symbol): string = $stringView(state, sym)
|
||||
|
||||
proc initGC*() {.importcpp: "nix::initGC", header: "eval.hh".}
|
|
@ -0,0 +1,7 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
{.passC: staticExec("pkg-config --cflags nix-main").}
|
||||
{.passL: staticExec("pkg-config --libs nix-main").}
|
||||
|
||||
proc initNix*() {.importcpp: "nix::initNix", header: "shared.hh".}
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
#include "eval.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
ref<EvalState> newEvalState(ref<Store> store)
|
||||
{
|
||||
auto searchPath = Strings();
|
||||
auto evalState =
|
||||
#if HAVE_BOEHMGC
|
||||
std::allocate_shared<EvalState>(
|
||||
traceable_allocator<EvalState>(), searchPath, store, store)
|
||||
#else
|
||||
std::make_shared<EvalState>(
|
||||
searchPath, store, store)
|
||||
#endif
|
||||
;
|
||||
return ref<EvalState>(evalState);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
type StringView* {.importcpp: "std::string_view", header: "<string>".} = object
|
||||
|
||||
proc toStringView*(s: pointer; count: int): StringView {.
|
||||
importcpp: "std::string_view(static_cast<const char *>(#), #)", constructor.}
|
||||
|
||||
proc toStringView*(s: string): StringView {.inline.} =
|
||||
if s.len == 0: toStringView(nil, 0)
|
||||
else: toStringView(unsafeAddr s[0], s.len)
|
||||
|
||||
proc toStringView*(buf: openarray[byte]): StringView {.inline.} =
|
||||
if buf.len == 0: toStringView(nil, 0)
|
||||
else: toStringView(unsafeAddr buf[0], buf.len)
|
||||
|
||||
proc toStringView*(sv: StringView): StringView {.inline.} = sv
|
||||
|
||||
proc data(sv: StringView): pointer {.importcpp.}
|
||||
proc size(sv: StringView): csize_t {.importcpp.}
|
||||
|
||||
proc `$`*(sv: StringView): string =
|
||||
result = newString(sv.size)
|
||||
copyMem(addr result[0], sv.data, result.len)
|
|
@ -23,7 +23,7 @@ var nixVersion* {.importc: "nix::nixVersion", header: "globals.hh".}: StdString
|
|||
proc isDerivation*(path: StorePath): bool {.importcpp.}
|
||||
|
||||
type
|
||||
Store {.importcpp: "nix::ref<nix::Store>", header: "store-api.hh".} = object
|
||||
Store* {.importcpp: "nix::ref<nix::Store>", header: "store-api.hh".} = object
|
||||
discard
|
||||
|
||||
proc ensurePath*(store: Store; path: StorePath) {.importcpp.}
|
|
@ -33,6 +33,7 @@ const
|
|||
STDERR_RESULT* = 0x52534C54
|
||||
|
||||
type WorkerOperation* = enum
|
||||
wopInvalid = 0,
|
||||
wopIsValidPath = 1,
|
||||
wopHasSubstitutes = 3,
|
||||
wopQueryPathHash = 4, # obsolete
|
||||
|
|
Loading…
Reference in New Issue