--- a/Cargo.lock +++ b/Cargo.lock @@ -729,22 +729,6 @@ ] [[package]] -name = "ctor" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" -dependencies = [ - "ctor-proc-macro", - "dtor", -] - -[[package]] -name = "ctor-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" - -[[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -916,21 +900,6 @@ ] [[package]] -name = "dtor" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e58a0764cddb55ab28955347b45be00ade43d4d6f3ba4bf3dc354e4ec9432934" -dependencies = [ - "dtor-proc-macro", -] - -[[package]] -name = "dtor-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" - -[[package]] name = "ed25519" version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1973,17 +1942,12 @@ version = "0.1.0" dependencies = [ "anyhow", - "better-command", "bindgen", - "ctor", "cxx", "cxx-build", - "futures", "itertools 0.14.0", "nixlike", "pkg-config", - "r2d2", - "regex", "serde", "serde_json", "test-log", @@ -1992,7 +1956,6 @@ "tokio-util", "tracing", "tracing-indicatif", - "unindent", ] [[package]] @@ -2454,17 +2417,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r2d2" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93" -dependencies = [ - "log", - "parking_lot", - "scheduled-thread-pool", -] [[package]] name = "rand" @@ -2829,15 +2781,6 @@ ] [[package]] -name = "scheduled-thread-pool" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" -dependencies = [ - "parking_lot", -] - -[[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3673,12 +3616,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - -[[package]] -name = "unindent" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" [[package]] name = "unit-prefix" --- a/cmds/fleet/Cargo.toml +++ b/cmds/fleet/Cargo.toml @@ -55,4 +55,5 @@ "dep:indicatif", "dep:human-repr", "better-command/indicatif", + "nix-eval/indicatif", ] --- a/cmds/fleet/src/cmds/build_systems.rs +++ b/cmds/fleet/src/cmds/build_systems.rs @@ -28,18 +28,12 @@ build_attr: String, } -async fn build_task( - config: Config, - hostname: String, - build_attr: &str, - // batch: Option, -) -> Result { +async fn build_task(config: Config, hostname: String, build_attr: &str) -> Result { info!("building"); let host = config.host(&hostname).await?; // let action = Action::from(self.subcommand.clone()); let nixos = host.nixos_config().await?; let drv = nix_go!(nixos.system.build[{ build_attr }]); - // let outputs = drv.build_maybe_batch(batch).await?; let out_output = drv.build("out").await?; // We already have system profiles for backups. @@ -66,17 +60,11 @@ let hosts = opts.filter_skipped(config.list_hosts().await?).await?; let set = LocalSet::new(); let build_attr = self.build_attr.clone(); - // let batch = (hosts.len() > 1).then(|| { - // config - // .nix_session - // .new_build_batch("build-hosts".to_string()) - // }); for host in hosts { let config = config.clone(); let span = info_span!("build", host = field::display(&host.name)); let hostname = host.name; let build_attr = build_attr.clone(); - // let batch = batch.clone(); set.spawn_local( (async move { let built = match build_task(config, hostname.clone(), &build_attr).await { @@ -107,11 +95,6 @@ pub async fn run(self, config: &Config, opts: &FleetOpts) -> Result<()> { let hosts = opts.filter_skipped(config.list_hosts().await?).await?; let set = LocalSet::new(); - // let batch = (hosts.len() > 1).then(|| { - // config - // .nix_session - // .new_build_batch("deploy-hosts".to_string()) - // }); for host in hosts.into_iter() { let config = config.clone(); let span = info_span!("deploy", host = field::display(&host.name)); --- a/cmds/fleet/src/cmds/secrets/mod.rs +++ b/cmds/fleet/src/cmds/secrets/mod.rs @@ -169,7 +169,6 @@ expected_owners: &[String], expected_generation_data: serde_json::Value, prefer_identities: &[String], - // batch: Option, ) -> Result { let original_set = secret.owners.clone(); @@ -207,12 +206,10 @@ field, expected_owners.to_vec(), expected_generation_data, - // batch, ) .await?; Ok(generated) } else { - // drop(batch); let identity_holder = if !prefer_identities.is_empty() { prefer_identities .iter() @@ -264,7 +261,6 @@ default_generator: Value, expected_owners: &[String], expected_generation_data: serde_json::Value, - // batch: Option, ) -> Result { let generator = nix_go!(secret.generator); let on: Option = nix_go_json!(default_generator.impureOn); @@ -292,7 +288,6 @@ let generator = nix_go!(call_package(generator)(Obj {})); - // let generator = generator.build_maybe_batch(batch).await?; let generator = generator.build("out").await?; let generator = host.remote_derivation(&generator).await?; @@ -347,12 +342,11 @@ secret: Value, expected_owners: &[String], expected_generation_data: serde_json::Value, - // batch: Option, ) -> Result { let generator = nix_go!(secret.generator); // Can't properly check on nix module system level { - let gen_ty = generator.type_of()?; + let gen_ty = generator.type_of(); if matches!(gen_ty, NixType::Null) { bail!("secret has no generator defined, can't automatically generate it."); } @@ -394,7 +388,6 @@ default_generator, expected_owners, expected_generation_data, - // batch, ) .await } @@ -416,7 +409,6 @@ secret: Value, expected_owners: Vec, expected_generation_data: serde_json::Value, - // batch: Option, ) -> Result { // let owners: Vec = nix_go_json!(secret.expectedOwners); Ok(FleetSharedSecret { @@ -426,7 +418,6 @@ secret, &expected_owners, expected_generation_data, - // batch, ) .await?, owners: expected_owners, @@ -747,7 +738,6 @@ let stored_shared_set = config.list_shared().into_iter().collect::>(); { // Generate missing shared - // let shared_batch = None; let _span = info_span!("shared").entered(); let expected_shared_set = config .list_configured_shared() @@ -772,7 +762,6 @@ secret, expected_owners, expected_generation_data, - // shared_batch.clone(), ) .in_current_span() .await?; @@ -780,7 +769,6 @@ } } if !skip_hosts { - // let hosts_batch = None; for host in config.list_hosts().await? { if opts.should_skip(&host).await? { continue; @@ -808,7 +796,6 @@ secret, slice::from_ref(&host.name), expected_generation_data, - // hosts_batch.clone(), ) .in_current_span() .await @@ -834,7 +821,6 @@ secret, slice::from_ref(&host.name), expected_generation_data, - // hosts_batch.clone(), ) .in_current_span() .await --- a/cmds/fleet/src/cmds/tf.rs +++ b/cmds/fleet/src/cmds/tf.rs @@ -1,8 +1,4 @@ -use std::{ - collections::{BTreeMap, HashMap}, - ffi::OsString, - path::PathBuf, -}; +use std::{collections::BTreeMap, ffi::OsString, path::PathBuf}; use anyhow::{Context, Result}; use clap::Parser; --- a/crates/nix-eval/Cargo.toml +++ b/crates/nix-eval/Cargo.toml @@ -7,26 +7,23 @@ [dependencies] anyhow.workspace = true -better-command.workspace = true nixlike.workspace = true serde = { workspace = true, features = ["derive"] } serde_json.workspace = true thiserror.workspace = true -tokio = { workspace = true, features = ["io-util", "process"] } +tokio = { workspace = true } tokio-util.workspace = true tracing.workspace = true cxx = "1.0.168" -futures = "0.3.31" itertools = "0.14.0" -r2d2 = "0.8.10" -regex = "1.11.1" test-log = { version = "0.2.18", features = ["trace"] } -unindent = "0.2.4" -tracing-indicatif = "0.3.13" -ctor = "0.5.0" +tracing-indicatif = { version = "0.3.13", optional = true } [build-dependencies] bindgen = "0.72.0" cxx-build = "1.0.168" pkg-config = "0.3.30" + +[features] +indicatif = ["dep:tracing-indicatif"] --- a/crates/nix-eval/src/lib.rs +++ b/crates/nix-eval/src/lib.rs @@ -1,8 +1,3 @@ -//! This whole library should be replaced with either binding to nix libexpr, -//! or with tvix (once it is able to build NixOS). -//! -//! Current api is awful, little effort was put into this implementation. - use std::borrow::Cow; use std::cell::RefCell; use std::ffi::{CStr, CString, c_char, c_int, c_uint, c_void}; @@ -11,7 +6,7 @@ use std::sync::LazyLock; use std::{collections::HashMap, path::PathBuf}; -use anyhow::{Context, bail}; +use anyhow::{Context, anyhow, bail}; use serde::Serialize; use serde::de::DeserializeOwned; @@ -27,17 +22,21 @@ flake_reference_parse_flags_new, flake_reference_parse_flags_set_base_directory, flake_settings, flake_settings_free, flake_settings_new, init_bool, init_int, init_string, locked_flake_free, locked_flake_get_output_attrs, set_err_msg, setting_set, state_free, - value_decref, value_force, value_incref, + value_decref, value_incref, }; -mod value; // Contains macros helpers pub mod logging; #[doc(hidden)] pub mod macros; pub mod util; -#[allow(non_upper_case_globals, non_camel_case_types, non_snake_case)] +#[allow( + non_upper_case_globals, + non_camel_case_types, + non_snake_case, + dead_code +)] mod nix_raw { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } @@ -88,6 +87,11 @@ } } +enum FunctorKind { + Function, + Functor, +} + #[derive(Debug)] #[repr(i32)] enum NixErrorKind { @@ -130,9 +134,9 @@ unsafe { nix_raw::GC_unregister_my_thread() }; } -struct ThreadRegisterGuard {} +pub struct ThreadRegisterGuard {} impl ThreadRegisterGuard { - fn new() -> Self { + pub fn new() -> Self { gc_register_my_thread(); Self {} } @@ -143,12 +147,12 @@ } } -struct NixContext(*mut c_context); +pub struct NixContext(*mut c_context); impl NixContext { - fn set_err(&mut self, err: NixErrorKind, msg: &CStr) { + pub fn set_err(&mut self, err: NixErrorKind, msg: &CStr) { unsafe { set_err_msg(self.0, err as c_int, msg.as_ptr()) }; } - fn new() -> Self { + pub fn new() -> Self { let ctx = unsafe { c_context_create() }; Self(ctx) } @@ -174,12 +178,6 @@ // but it looks ugly let str = unsafe { nix_raw::err_msg(null_mut(), self.0, null_mut()) }; Some(unsafe { CStr::from_ptr(str) }.to_string_lossy()) - - // TODO: There is also nix_err_info_msg, but I don't understand when it should be used - // Some(match self.error_kind()? { - // NixErrorKind::Generic => { - // } - // }) } fn clean_err(&mut self) { unsafe { @@ -210,6 +208,8 @@ } } struct GlobalState { + // Store should be valid as long as EvalState is valid + #[allow(dead_code)] store: Store, state: EvalState, } @@ -269,7 +269,7 @@ v } -fn set_setting(s: &CStr, v: &CStr) -> Result<()> { +pub fn set_setting(s: &CStr, v: &CStr) -> Result<()> { with_default_context(|c, _| unsafe { setting_set(c, s.as_ptr(), v.as_ptr()) }).map(|_| ()) } @@ -289,6 +289,13 @@ } unsafe impl Send for FetchSettings {} unsafe impl Sync for FetchSettings {} + +impl Default for FetchSettings { + fn default() -> Self { + Self::new() + } +} + impl Drop for FetchSettings { fn drop(&mut self) { unsafe { fetchers_settings_free(self.0) }; @@ -310,13 +317,13 @@ } } -struct FlakeReferenceParseFlags(*mut flake_reference_parse_flags); +pub struct FlakeReferenceParseFlags(*mut flake_reference_parse_flags); impl FlakeReferenceParseFlags { - fn new(settings: &mut FlakeSettings) -> Result { + pub fn new(settings: &mut FlakeSettings) -> Result { with_default_context(|c, _| unsafe { flake_reference_parse_flags_new(c, settings.0) }) .map(Self) } - fn set_base_dir(&mut self, dir: &str) -> Result<()> { + pub fn set_base_dir(&mut self, dir: &str) -> Result<()> { with_default_context(|c, _| { unsafe { flake_reference_parse_flags_set_base_directory( @@ -361,19 +368,9 @@ unsafe impl Sync for Store {} struct EvalState(*mut nix_raw::EvalState); -impl EvalState { - // TODO: store ownership - fn new_raw(store: *mut nix_raw::Store) -> Result { - let builder = - with_default_context(|c, _| unsafe { nix_raw::eval_state_builder_new(c, store) })?; - - with_default_context(|c, _| unsafe { eval_state_build(c, builder) }).map(Self) - - // with_default_context(|c| state_create(c)) - } -} unsafe impl Send for EvalState {} unsafe impl Sync for EvalState {} + impl Drop for EvalState { fn drop(&mut self) { unsafe { @@ -386,9 +383,7 @@ impl FlakeReference { pub fn new(s: &str, fetch: &FetchSettings) -> Result<(Self, String)> { let mut flake_settings = FlakeSettings::new()?; - let mut parse_flags = FlakeReferenceParseFlags::new(&mut flake_settings)?; - - // parse_flags.set_base_dir("/home/lach/build/fleet")?; + let parse_flags = FlakeReferenceParseFlags::new(&mut flake_settings)?; let mut out = null_mut(); let mut fragment = String::new(); @@ -461,16 +456,16 @@ } impl RealisedString { - fn as_str(&self) -> &str { + pub fn as_str(&self) -> &str { let len = unsafe { nix_raw::realised_string_get_buffer_size(self.0) }; let data: *const u8 = unsafe { nix_raw::realised_string_get_buffer_start(self.0) }.cast(); let data = unsafe { std::slice::from_raw_parts(data, len) }; std::str::from_utf8(data).expect("non-utf8 strings not supported") } - fn path_count(&self) -> usize { + pub fn path_count(&self) -> usize { unsafe { nix_raw::realised_string_get_store_path_count(self.0) } } - fn path(&self, i: usize) -> String { + pub fn path(&self, i: usize) -> String { assert!(i < self.path_count()); let path = unsafe { nix_raw::realised_string_get_store_path(self.0, i) }; let mut err_out = String::new(); @@ -482,8 +477,7 @@ unsafe impl Send for RealisedString {} impl Drop for RealisedString { fn drop(&mut self) { - with_default_context(|c, _| unsafe { nix_raw::realised_string_free(self.0) }) - .expect("string free should not fail") + unsafe { nix_raw::realised_string_free(self.0) } } } @@ -541,46 +535,53 @@ } impl Value { - pub fn new_attrs(v: HashMap<&str, Value>) -> Result { - let out = Self::new_uninit()?; + pub fn new_attrs(v: HashMap<&str, Value>) -> Self { + let out = Self::new_uninit(); let mut b = AttrsBuilder::new(v.len()); for (k, v) in v { b.insert(&k, v); } - with_default_context(|c, _| unsafe { nix_raw::make_attrs(c, out.0, b.0) })?; - Ok(out) + with_default_context(|c, _| unsafe { nix_raw::make_attrs(c, out.0, b.0) }) + .expect("attrs initialization should not fail"); + out } - fn new_list>(v: Vec) -> Result { + fn new_list>(v: Vec) -> Self { todo!() } - fn new_uninit() -> Result { - let out = with_default_context(|c, es| unsafe { alloc_value(c, es) })?; - Ok(Self(out)) + fn new_uninit() -> Self { + let out = with_default_context(|c, es| unsafe { alloc_value(c, es) }) + .expect("value allocation should not fail"); + Self(out) } - fn new_str(v: &str) -> Result { + pub fn new_str(v: &str) -> Self { let s = CString::new(v).expect("string should not contain NULs"); - let uninit = Self::new_uninit()?; + let out = Self::new_uninit(); // String is copied, `s` is free to be dropped - with_default_context(|c, _| unsafe { init_string(c, uninit.0, s.as_ptr()) })?; - Ok(uninit) + with_default_context(|c, _| unsafe { init_string(c, out.0, s.as_ptr()) }) + .expect("string initialization should not fail"); + out } - fn new_int(i: i64) -> Result { - let uninit = Self::new_uninit()?; - with_default_context(|c, _| unsafe { init_int(c, uninit.0, i) })?; - Ok(uninit) + pub fn new_int(i: i64) -> Self { + let out = Self::new_uninit(); + with_default_context(|c, _| unsafe { init_int(c, out.0, i) }) + .expect("int initialization should not fail"); + out } - fn new_bool(v: bool) -> Result { - let uninit = Self::new_uninit()?; - with_default_context(|c, _| unsafe { init_bool(c, uninit.0, v) })?; - Ok(uninit) + pub fn new_bool(v: bool) -> Self { + let out = Self::new_uninit(); + with_default_context(|c, _| unsafe { init_bool(c, out.0, v) }) + .expect("bool initialization should not fail"); + out } - fn force(&mut self, st: &mut EvalState) -> Result<()> { - with_default_context(|c, _| unsafe { value_force(c, st.0, self.0) })?; - Ok(()) - } - pub fn type_of(&self) -> Result { - let ty = with_default_context(|c, _| unsafe { nix_raw::get_type(c, self.0) })?; - Ok(NixType::from_int(ty)) + // TODO: As far as I can see, there is no way to get Thunks from nix public C api, so this function is useless + // fn force(&mut self, st: &mut EvalState) -> Result<()> { + // with_default_context(|c, _| unsafe { value_force(c, st.0, self.0) })?; + // Ok(()) + // } + pub fn type_of(&self) -> NixType { + let ty = with_default_context(|c, _| unsafe { nix_raw::get_type(c, self.0) }) + .expect("get_type should not fail"); + NixType::from_int(ty) } pub fn to_string(&self) -> Result { Ok(self.to_realised_string()?.as_str().to_owned()) @@ -608,7 +609,7 @@ // nix_raw::real // } pub fn list_fields(&self) -> Result> { - if !matches!(self.type_of()?, NixType::Attrs) { + if !matches!(self.type_of(), NixType::Attrs) { bail!("invalid type: expected attrs"); } @@ -625,7 +626,7 @@ Ok(out) } pub fn get_elem(&self, v: usize) -> Result { - if !matches!(self.type_of()?, NixType::List) { + if !matches!(self.type_of(), NixType::List) { bail!("invalid type: expected list"); } let len = @@ -659,10 +660,10 @@ for f in b_fields.iter() { out.insert(f.as_str(), other.get_field(f)?); } - Self::new_attrs(out) + Ok(Self::new_attrs(out)) } pub fn get_field(&self, name: impl AsFieldName) -> Result { - if !matches!(self.type_of()?, NixType::Attrs) { + if !matches!(self.type_of(), NixType::Attrs) { bail!("invalid type: expected attrs"); } @@ -675,19 +676,33 @@ .with_context(|| format!("getting field {:?}", name.to_field_name())) } pub fn call(&self, v: Value) -> Result { - if !matches!(self.type_of()?, NixType::Function) { - // TODO: Functors - bail!("invalid type: expected function"); - } + let kind = self + .functor_kind() + .ok_or_else(|| anyhow!("can only call function or functor"))?; + + let function = match kind { + FunctorKind::Function => self.clone(), + FunctorKind::Functor => { + let f = self.get_field("__functor")?; + assert_eq!( + f.type_of(), + NixType::Function, + "invalid functor encountered" + ); + f + } + }; - let out = Value::new_uninit()?; - with_default_context(|c, es| unsafe { nix_raw::value_call(c, es, self.0, v.0, out.0) })?; + let out = Value::new_uninit(); + with_default_context(|c, es| unsafe { + nix_raw::value_call(c, es, function.0, v.0, out.0) + })?; Ok(out) } pub fn eval(v: &str) -> Result { let s = CString::new(v).expect("expression shouldn't have internal NULs"); - let out = Self::new_uninit()?; + let out = Self::new_uninit(); with_default_context(|c, es| unsafe { expr_eval_from_string(c, es, s.as_ptr(), c"/homeless-shelter".as_ptr(), out.0) })?; @@ -723,13 +738,13 @@ } // Convert to string/evaluate derivations/etc - fn to_string_weak(&self) -> Result { - // TODO - self.to_string() - } + // fn to_string_weak(&self) -> Result { + // // TODO: For now, it works exactly like to_string, see the comment for fn force() + // self.to_string() + // } fn is_derivation(&self) -> bool { - if !matches!(self.type_of(), Ok(NixType::Attrs)) { + if !matches!(self.type_of(), NixType::Attrs) { return false; } let Some(ty) = self.get_field("type").ok() else { @@ -737,21 +752,34 @@ }; matches!(ty.to_string().as_deref(), Ok("derivation")) } + fn functor_kind(&self) -> Option { + match self.type_of() { + NixType::Attrs => self + .has_field("__functor") + .expect("has_field shouldn't fail for attrs") + .then_some(FunctorKind::Functor), + NixType::Function => Some(FunctorKind::Function), + _ => None, + } + } + pub fn is_function(&self) -> bool { + self.functor_kind().is_some() + } } impl From for Value { fn from(value: String) -> Self { - Value::new_str(&value).expect("todo: TryFrom") + Value::new_str(&value) } } impl From for Value { fn from(value: bool) -> Self { - Value::new_bool(value).expect("todo: TryFrom") + Value::new_bool(value) } } impl From<&str> for Value { fn from(value: &str) -> Self { - Value::new_str(&value).expect("todo: TryFrom") + Value::new_str(value) } } impl From> for Value @@ -759,7 +787,7 @@ T: Into, { fn from(value: Vec) -> Self { - Value::new_list(value).expect("todo: TryFrom") + Value::new_list(value) } } @@ -793,102 +821,24 @@ #[test_log::test] fn test_native() -> Result<()> { + init_libraries(); + let mut fetch_settings = FetchSettings::new(); fetch_settings.set(c"warn-dirty", c"false"); - // - let (mut r, _) = FlakeReference::new("/home/lach/build/fleet", &fetch_settings)?; + let manifest = format!("{}/../../", env!("CARGO_MANIFEST_DIR")); + let (mut r, _) = FlakeReference::new(&manifest, &fetch_settings)?; let locked = r.lock(&fetch_settings)?; let attrs = locked.get_attrs(&mut FlakeSettings::new()?)?; let builtins = Value::eval("builtins")?; - dbg!(builtins.type_of()?); + assert_eq!(builtins.type_of(), NixType::Attrs); - dbg!(attrs.type_of()?); - dbg!(attrs.list_fields()?); - dbg!( - attrs - .get_field("packages")? - .get_field("x86_64-linux")? - .get_field("fleet")? - .get_field("outPath")? - .to_string() - ); + assert_eq!(attrs.type_of(), NixType::Attrs); + let test_data = nix_go!(attrs.testData); + let test_string: String = nix_go_json!(test_data.testString); + assert_eq!(test_string, "hello"); + Ok(()) } - -// struct NixBuildTask(Value, oneshot::Sender>>); -// -// #[derive(Clone)] -// pub struct NixBuildBatch { -// tx: mpsc::UnboundedSender, -// } -// -// #[instrument(skip(values))] -// async fn build_multiple(name: String, values: Vec) -> Result<()> { -// let builtins = Value::eval("builtins")?; -// let drv = nix_go!(builtins.derivation(Obj { -// // FIXME: pass system from localSystem or fleet args -// // system, -// name, -// builder: "/bin/sh", -// // we want nothing from this derivation, it is only used to perform multiple builds at once. -// args: vec!["-c", "echo > $out"], -// preferLocalBuild: true, -// allowSubstitutes: false, -// buildInputs: values, -// })); -// drv.build()?; -// Ok(()) -// } -// -// impl NixBuildBatch { -// fn new(name: String) -> Self { -// let (tx, mut rx) = mpsc::unbounded_channel::(); -// -// tokio::task::spawn(async move { -// let mut deps = vec![]; -// let mut build_data = vec![]; -// while let Some(task) = rx.recv().await { -// build_data.push(task.0.clone()); -// deps.push(task); -// } -// if deps.is_empty() { -// return; -// } -// match build_multiple(name, build_data).await { -// Ok(_) => { -// for NixBuildTask(v, o) in deps { -// let _ = o.send(v.build()); -// } -// } -// Err(e) => { -// for NixBuildTask(v, o) in deps { -// let s = v.to_string_weak(); -// let s = match s { -// Ok(s) => s, -// Err(e) => { -// let _ = o.send(Err(e)); -// continue; -// } -// }; -// if PathBuf::from(s).exists() { -// let _ = o.send(v.build()); -// } else { -// let _ = o.send(Err(e.clone())); -// } -// } -// } -// }; -// }); -// Self { tx } -// } -// pub async fn submit(self, task: Value) -> Result> { -// let Self { tx: task_tx } = self; -// let (tx, rx) = oneshot::channel(); -// let _ = task_tx.send(NixBuildTask(task, tx)); -// drop(task_tx); -// rx.await.expect("shoudn't be cancelled here") -// } -// } --- a/crates/nix-eval/src/logging.rs +++ b/crates/nix-eval/src/logging.rs @@ -3,9 +3,10 @@ use std::sync::{LazyLock, Mutex}; use tracing::{ - Level, Metadata, Span, debug, debug_span, error, error_span, event, info, info_span, trace, - trace_span, warn, warn_span, + Level, Span, debug, debug_span, error, error_span, info, info_span, trace, trace_span, warn, + warn_span, }; +#[cfg(feature = "indicatif")] use tracing_indicatif::span_ext::IndicatifSpanExt as _; #[derive(Debug)] @@ -31,8 +32,7 @@ } fn parse_path(path: &str) -> &str { - let path = strip_prefix_suffix(path, "\x1b[35;1m", "\x1b[0m").unwrap_or(path); - path + strip_prefix_suffix(path, "\x1b[35;1m", "\x1b[0m").unwrap_or(path) } fn parse_drv(drv: &str) -> &str { @@ -245,9 +245,9 @@ Debug, Vomit, } -impl Into for Verbosity { - fn into(self) -> tracing::Level { - match self { +impl From for tracing::Level { + fn from(val: Verbosity) -> Self { + match val { Verbosity::Error => Level::ERROR, Verbosity::Warn => Level::WARN, Verbosity::Notice => Level::WARN, @@ -277,126 +277,9 @@ warn!("unknown log level: {u}"); Verbosity::Vomit }) - } -} - -#[derive(Hash, PartialEq, Eq, Clone, Copy)] -enum MetadataKind { - Span, - Event, -} -// impl MetadataKind { -// fn kind(&self) -> Kind { -// match self { -// MetadataKind::Span => Kind::SPAN, -// MetadataKind::Event => Kind::EVENT, -// } -// } -// } - -#[derive(Hash, PartialEq, Eq)] -struct ForeignMetadataInfo { - target: &'static str, - level: Level, - kind: MetadataKind, - name: &'static str, - module: Option<&'static str>, - file: Option<&'static str>, - line: Option, - names: &'static [&'static str], -} - -struct FakeCallsite; -impl tracing::callsite::Callsite for FakeCallsite { - fn set_interest(&self, interest: tracing::subscriber::Interest) { - unreachable!() - } - - fn metadata(&self) -> &Metadata<'_> { - unreachable!() - } -} -const FAKE_CALLSITE: FakeCallsite = FakeCallsite; - -#[cfg(false)] -#[derive(Default)] -struct ForeignSpanData { - interned: HashSet<&'static str>, - metadatas: HashMap>, -} -#[cfg(false)] -impl ForeignSpanData { - fn intern(&mut self, s: &str) -> &'static str { - if let Some(v) = self.interned.get(s) { - return *v; - } - let leaked: Box = s.into(); - let leaked = Box::leak(leaked); - self.interned.insert(leaked); - return leaked; - } - fn alloc_metadata<'t>( - &'t mut self, - target: &'static str, - level: Level, - kind: MetadataKind, - name: &'static str, - module: Option<&'static str>, - file: Option<&'static str>, - line: Option, - names: &'static [&'static str], - ) -> &'static Metadata<'static> { - let info = ForeignMetadataInfo { - target, - level, - kind, - name, - module, - file, - line, - names, - }; - if let Some(v) = self.metadatas.get(&info) { - return *v; - } - let fake = FakeCallsite; - let metadata = Box::leak::<'static>(Box::new(Metadata::new( - name, - target, - level, - file, - line, - module, - FieldSet::new(names, tracing::callsite::Identifier(&FAKE_CALLSITE)), - kind.kind(), - ))); - - let meta_raw = &raw const *metadata; - let fields_raw = &raw const *metadata.fields(); - - // SAFETY: FieldSet struct should be inside of metadata struct... Which we assume here, but do not test - // FIXME: Safety comment above might be invalidated at any time, this should actually be covered by unit test (or, better: runtime assertion... Somehow.) - let fields_offset = unsafe { fields_raw.cast::().offset_from(meta_raw.cast()) }; - let field_set = unsafe { - ((&raw mut *metadata).cast::<()>()) - .byte_offset(fields_offset) - .cast::
() - }; - // FIXME: metadata borrow here invalidates our &mut borrow of 'static Metadata, and 'static FieldSet so this construction should be replaced with raw pointers or idk. - // Something should be better done inside of tracing crate itself, someting like interior mutability. - let callsite = Box::leak(Box::new(tracing::callsite::DefaultCallsite::new(metadata))); - unsafe { *field_set = FieldSet::new(names, tracing::callsite::Identifier(callsite)) }; - - tracing::callsite::register(&*callsite); - - self.metadatas.insert(info, metadata); - return metadata; } } -#[cfg(false)] -static FOREIGN_SPAN_DATA: LazyLock> = - LazyLock::new(|| Mutex::new(ForeignSpanData::default())); static NIX_SPAN_MAPPING: LazyLock>> = LazyLock::new(|| Mutex::new(HashMap::new())); @@ -491,7 +374,10 @@ } }; if !s.trim().is_empty() { - span.pb_set_message(s); + #[cfg(feature = "indicatif")] + { + span.pb_set_message(s); + } let _e = span.enter(); let level: Level = self.verbosity.into(); if level == Level::ERROR { @@ -506,12 +392,15 @@ trace!(target: "nix", "{}", s) } } else { - span.pb_start(); + #[cfg(feature = "indicatif")] + { + span.pb_start(); + } } mapping.insert(self.activity_id, span); } fn emit_result(&mut self, ty: u32) { - let mut mapping = NIX_SPAN_MAPPING.lock().expect("not poisoned"); + let mapping = NIX_SPAN_MAPPING.lock().expect("not poisoned"); let Some(parent) = mapping.get(&self.activity_id) else { panic!("unexpected result for dead parent"); @@ -536,9 +425,12 @@ // parent.pb_set_message(phase); debug!(target: "nix::phase", phase) } - (ResultType::Progress, [Int(done), Int(expected), Int(_), Int(_)]) => { - parent.pb_set_length(*expected as u64); - parent.pb_set_position(*done as u64); + (ResultType::Progress, [Int(_done), Int(_expected), Int(_), Int(_)]) => { + #[cfg(feature = "indicatif")] + { + parent.pb_set_length(*_expected as u64); + parent.pb_set_position(*_done as u64); + } } _ => warn!("unknown progress report: {:?}({:?})", &res, &self.fields), } @@ -575,10 +467,6 @@ trace!(target: "nix", "{v}") } } - -// fn start_activity(act: u64, lvl: u32, act_ty: u32, s: &str, parent: u32) { -// tracing::Span::new(meta, values) -// } #[cxx::bridge] pub mod nix_logging_cxx { --- a/crates/nix-eval/src/macros.rs +++ b/crates/nix-eval/src/macros.rs @@ -20,7 +20,7 @@ use $crate::{nix_expr_inner}; let mut out = std::collections::hash_map::HashMap::new(); nix_expr_inner!(@obj(out) $($tt)*); - Value::new_attrs(out)? + Value::new_attrs(out) }}; (@field($o:ident) . $var:ident $($tt:tt)*) => {{ $o.index_attr(stringify!($var)); --- a/crates/nix-eval/src/value.rs +++ /dev/null @@ -1,6 +0,0 @@ -use std::{collections::HashMap, fmt, path::PathBuf, sync::Arc}; - -use better_command::NixHandler; -use serde::{Serialize, de::DeserializeOwned}; - -use crate::{Result, Value, nix_go}; --- a/flake.lock +++ b/flake.lock @@ -104,14 +104,18 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1756860322, - "narHash": "sha256-mT01CpWVdqSm79L270dSkjdYbdc37r+Hq9vk4GTp7Ao=", - "path": "/home/lach/build/nix-src", - "type": "path" + "lastModified": 1757000273, + "narHash": "sha256-9AKhwsSlegWnNFy8++OMNctrxJUUIE7nG4s4ZHmFPic=", + "owner": "deltarocks", + "repo": "nix", + "rev": "eba1f549ec21208cf98343f1351a95e2e6eb3fbb", + "type": "github" }, "original": { - "path": "/home/lach/build/nix-src", - "type": "path" + "owner": "deltarocks", + "ref": "fleet", + "repo": "nix", + "type": "github" } }, "nixpkgs": { --- a/flake.nix +++ b/flake.nix @@ -19,7 +19,7 @@ }; # DeterminateSystem's nix fork is controversial, but I don't mind it, # and it has lazy-trees support which is useful for fleet. - nix.url = "/home/lach/build/nix-src"; + nix.url = "github:deltarocks/nix/fleet"; }; outputs = inputs: @@ -44,10 +44,13 @@ fleetModules.tf = ./modules/extras/tf.nix; - testObj = { - v = "Hello"; + # Used to test nix-eval bindings + testData = { + testObj = { + v = "Hello"; + }; + testString = "hello"; }; - testString = "hello"; # To be used with https://github.com/NixOS/nix/pull/8892 schemas =