From a8f1c81b60d964c34d014a846ac3978d7da3f43b Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Wed, 11 Feb 2026 23:55:24 +0800 Subject: [PATCH] fix: derivation (WIP) --- nix-js/runtime-ts/src/builtins/derivation.ts | 13 -------- nix-js/runtime-ts/src/builtins/index.ts | 2 +- nix-js/runtime-ts/src/thunk.ts | 6 ++-- nix-js/src/context.rs | 17 ++++++++++- nix-js/src/error.rs | 11 +++++++ nix-js/src/fetcher.rs | 17 ++++++----- nix-js/src/fetcher/git.rs | 6 ++-- nix-js/src/lib.rs | 2 +- nix-js/src/runtime/corepkgs/derivation.nix | 31 ++++++++++++++++++++ nix-js/src/runtime/ops.rs | 19 +++++------- nix-js/src/store/config.rs | 4 +-- nix-js/src/store/daemon.rs | 10 ++----- 12 files changed, 89 insertions(+), 49 deletions(-) create mode 100644 nix-js/src/runtime/corepkgs/derivation.nix diff --git a/nix-js/runtime-ts/src/builtins/derivation.ts b/nix-js/runtime-ts/src/builtins/derivation.ts index 6be5659..9374e2e 100644 --- a/nix-js/runtime-ts/src/builtins/derivation.ts +++ b/nix-js/runtime-ts/src/builtins/derivation.ts @@ -127,12 +127,7 @@ const structuredAttrsExcludedKeys = new Set([ ]); const specialAttrs = new Set([ - "name", - "builder", - "system", "args", - "outputs", - "__structuredAttrs", "__ignoreNulls", "__contentAddressed", "__impure", @@ -300,14 +295,6 @@ export const derivationStrict = (args: NixValue): NixAttrs => { const drvArgs = extractArgs(attrs, collectedContext); const env = extractEnv(attrs, structuredAttrs, ignoreNulls, collectedContext, drvName); - if (!structuredAttrs) { - env.set("name", drvName); - env.set("builder", builder); - env.set("system", platform); - if (outputs.length > 1 || outputs[0] !== "out") { - env.set("outputs", outputs.join(" ")); - } - } const { inputDrvs, inputSrcs } = extractInputDrvsAndSrcs(collectedContext); diff --git a/nix-js/runtime-ts/src/builtins/index.ts b/nix-js/runtime-ts/src/builtins/index.ts index 43f0ab3..81c62ba 100644 --- a/nix-js/runtime-ts/src/builtins/index.ts +++ b/nix-js/runtime-ts/src/builtins/index.ts @@ -193,7 +193,7 @@ export const builtins: any = { warn: mkPrimop(functional.warn, "warn", 2), break: mkPrimop(functional.breakFunc, "break", 1), - derivation: mkPrimop(derivation.derivation, "derivation", 1), + derivation: undefined as any, derivationStrict: mkPrimop(derivation.derivationStrict, "derivationStrict", 1), import: mkPrimop(io.importFunc, "import", 1), diff --git a/nix-js/runtime-ts/src/thunk.ts b/nix-js/runtime-ts/src/thunk.ts index d3fb5c5..c4cf7ae 100644 --- a/nix-js/runtime-ts/src/thunk.ts +++ b/nix-js/runtime-ts/src/thunk.ts @@ -6,7 +6,7 @@ import type { NixValue, NixThunkInterface, NixStrictValue } from "./types"; import { HAS_CONTEXT } from "./string-context"; import { IS_PATH } from "./types"; -import { isAttrs } from "./builtins/type-check"; +import { isAttrs, isList } from "./builtins/type-check"; /** * Symbol used to mark objects as thunks @@ -165,7 +165,9 @@ export const forceDeep = (value: NixValue, seen: WeakSet = new WeakSet() } return CYCLE_MARKER; } - seen.add(forced); + if (isAttrs(forced) || isList(forced)) { + seen.add(forced); + } if (HAS_CONTEXT in forced || IS_PATH in forced) { return forced; diff --git a/nix-js/src/context.rs b/nix-js/src/context.rs index c7a4b7f..e722983 100644 --- a/nix-js/src/context.rs +++ b/nix-js/src/context.rs @@ -41,7 +41,22 @@ impl Context { let ctx = Ctx::new()?; let runtime = Runtime::new()?; - Ok(Self { ctx, runtime }) + let mut context = Self { ctx, runtime }; + context.init_derivation()?; + + Ok(context) + } + + fn init_derivation(&mut self) -> Result<()> { + const DERIVATION_NIX: &str = include_str!("runtime/corepkgs/derivation.nix"); + let source = Source::new_virtual( + "".into(), + DERIVATION_NIX.to_string(), + ); + let code = self.ctx.compile(source, None)?; + self.runtime + .eval(format!("Nix.builtins.derivation = {}", code), &mut self.ctx)?; + Ok(()) } eval!(eval, "Nix.force({})"); diff --git a/nix-js/src/error.rs b/nix-js/src/error.rs index 06a58a6..e43f639 100644 --- a/nix-js/src/error.rs +++ b/nix-js/src/error.rs @@ -21,6 +21,8 @@ pub enum SourceType { Repl(Arc), /// file File(Arc), + /// virtual (name, no path) + Virtual(Arc), } #[derive(Clone, Debug)] @@ -71,6 +73,13 @@ impl Source { }) } + pub fn new_virtual(name: Arc, src: String) -> Self { + Self { + ty: SourceType::Virtual(name), + src: src.into(), + } + } + pub fn get_dir(&self) -> &Path { use SourceType::*; match &self.ty { @@ -79,6 +88,7 @@ impl Source { .as_path() .parent() .expect("source file must have a parent dir"), + Virtual(_) => Path::new("/"), } } @@ -87,6 +97,7 @@ impl Source { SourceType::Eval(_) => "«eval»".into(), SourceType::Repl(_) => "«repl»".into(), SourceType::File(path) => path.as_os_str().to_string_lossy().to_string(), + SourceType::Virtual(name) => name.to_string(), } } } diff --git a/nix-js/src/fetcher.rs b/nix-js/src/fetcher.rs index afe0699..c745e27 100644 --- a/nix-js/src/fetcher.rs +++ b/nix-js/src/fetcher.rs @@ -264,14 +264,15 @@ pub fn op_fetch_tarball( ); if let Some(ref expected) = expected_sha256 - && nar_hash != *expected { - return Err(NixRuntimeError::from(format!( - "NAR hash mismatch for '{}': expected {}, got {}", - url, - expected_hex.expect("must be Some"), - nar_hash_hex - ))); - } + && nar_hash != *expected + { + return Err(NixRuntimeError::from(format!( + "NAR hash mismatch for '{}': expected {}, got {}", + url, + expected_hex.expect("must be Some"), + nar_hash_hex + ))); + } info!("Adding to store"); let ctx: &Ctx = state.get_ctx(); diff --git a/nix-js/src/fetcher/git.rs b/nix-js/src/fetcher/git.rs index c5be5a2..2fda51c 100644 --- a/nix-js/src/fetcher/git.rs +++ b/nix-js/src/fetcher/git.rs @@ -31,8 +31,10 @@ pub fn fetch_git( let temp_dir = tempfile::tempdir()?; let checkout_dir = checkout_rev_to_temp(&bare_repo, &target_rev, submodules, temp_dir.path())?; - let nar_hash = hex::encode(crate::nar::compute_nar_hash(&checkout_dir) - .map_err(|e| GitError::NarHashError(e.to_string()))?); + let nar_hash = hex::encode( + crate::nar::compute_nar_hash(&checkout_dir) + .map_err(|e| GitError::NarHashError(e.to_string()))?, + ); let store_path = store .add_to_store_from_path(name, &checkout_dir, vec![]) diff --git a/nix-js/src/lib.rs b/nix-js/src/lib.rs index 0f00160..3508ac1 100644 --- a/nix-js/src/lib.rs +++ b/nix-js/src/lib.rs @@ -10,9 +10,9 @@ mod downgrade; mod fetcher; mod ir; mod nar; +mod nix_utils; mod runtime; mod store; -mod nix_utils; #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; diff --git a/nix-js/src/runtime/corepkgs/derivation.nix b/nix-js/src/runtime/corepkgs/derivation.nix new file mode 100644 index 0000000..a7c4dda --- /dev/null +++ b/nix-js/src/runtime/corepkgs/derivation.nix @@ -0,0 +1,31 @@ +drvAttrs@{ + outputs ? [ "out" ], + ... +}: + +let + + strict = derivationStrict drvAttrs; + + commonAttrs = + drvAttrs + // (builtins.listToAttrs outputsList) + // { + all = map (x: x.value) outputsList; + inherit drvAttrs; + }; + + outputToAttrListElement = outputName: { + name = outputName; + value = commonAttrs // { + outPath = builtins.getAttr outputName strict; + drvPath = strict.drvPath; + type = "derivation"; + inherit outputName; + }; + }; + + outputsList = map outputToAttrListElement outputs; + +in +(builtins.head outputsList).value diff --git a/nix-js/src/runtime/ops.rs b/nix-js/src/runtime/ops.rs index e4fa9cb..507c00d 100644 --- a/nix-js/src/runtime/ops.rs +++ b/nix-js/src/runtime/ops.rs @@ -1,6 +1,5 @@ use std::path::{Component, Path, PathBuf}; use std::str::FromStr; -use std::sync::Arc; use hashbrown::hash_map::{Entry, HashMap}; @@ -53,12 +52,12 @@ pub(super) fn op_import( let corepkg_name = &path[5..path.len() - 1]; if let Some(file) = CorePkgs::get(corepkg_name) { tracing::info!("Importing corepkg: {}", corepkg_name); - let source = Source { - ty: crate::error::SourceType::Eval(Arc::new(ctx.get_current_dir().to_path_buf())), - src: str::from_utf8(&file.data) + let source = Source::new_virtual( + path.into(), + str::from_utf8(&file.data) .expect("corrupted corepkgs file") .into(), - }; + ); ctx.add_source(source.clone()); return Ok(ctx.compile(source).map_err(|err| err.to_string())?); } else { @@ -445,9 +444,8 @@ pub(super) fn op_add_path( }; if let Some(ref expected_hash) = sha256 { - let expected_hex = - NixHash::from_str(expected_hash, Some(HashAlgo::Sha256)) - .map_err(|err| err.to_string())?; + let expected_hex = NixHash::from_str(expected_hash, Some(HashAlgo::Sha256)) + .map_err(|err| err.to_string())?; if computed_hash != expected_hex.digest_as_bytes() { return Err(NixRuntimeError::from(format!( "hash mismatch for path '{}': expected {}, got {}", @@ -960,9 +958,8 @@ pub(super) fn op_add_filtered_path( }; if let Some(ref expected_hash) = sha256 { - let expected_hex = - NixHash::from_str(expected_hash, Some(HashAlgo::Sha256)) - .map_err(|err| err.to_string())?; + let expected_hex = NixHash::from_str(expected_hash, Some(HashAlgo::Sha256)) + .map_err(|err| err.to_string())?; if computed_hash != expected_hex.digest_as_bytes() { return Err(NixRuntimeError::from(format!( "hash mismatch for path '{}': expected {}, got {}", diff --git a/nix-js/src/store/config.rs b/nix-js/src/store/config.rs index 059d694..2f539e2 100644 --- a/nix-js/src/store/config.rs +++ b/nix-js/src/store/config.rs @@ -11,9 +11,7 @@ impl StoreConfig { .map(PathBuf::from) .unwrap_or_else(|_| PathBuf::from("/nix/var/nix/daemon-socket/socket")); - Self { - daemon_socket, - } + Self { daemon_socket } } } diff --git a/nix-js/src/store/daemon.rs b/nix-js/src/store/daemon.rs index 0cde362..15fc66d 100644 --- a/nix-js/src/store/daemon.rs +++ b/nix-js/src/store/daemon.rs @@ -8,13 +8,13 @@ use nix_compat::wire::ProtocolVersion; use nix_compat::wire::de::{NixRead, NixReader}; use nix_compat::wire::ser::{NixSerialize, NixWrite, NixWriter, NixWriterBuilder}; use num_enum::{IntoPrimitive, TryFromPrimitive}; +use thiserror::Error; use tokio::io::{AsyncReadExt, AsyncWriteExt, ReadHalf, WriteHalf, split}; use tokio::net::UnixStream; use tokio::sync::Mutex; -use thiserror::Error; -use crate::error::{Error, Result}; use super::Store; +use crate::error::{Error, Result}; pub struct DaemonStore { runtime: tokio::runtime::Runtime, @@ -569,11 +569,7 @@ impl NixDaemonClient { traces.push(trace_hint); } - Ok(NixDaemonError { - level, - msg, - traces, - }) + Ok(NixDaemonError { level, msg, traces }) } /// Check if a path is valid in the store