diff options
author | Florian Klink <flokli@flokli.de> | 2024-01-11T13·44+0200 |
---|---|---|
committer | flokli <flokli@flokli.de> | 2024-01-12T22·25+0000 |
commit | d516ce56b1fe8b765e8833edb1568817158b306f (patch) | |
tree | 93ed2c09db534daf69ae7453474cb5526601dcd8 /tvix | |
parent | 82540717d66a0b0f021763766571fc6c418d2427 (diff) |
feat(tvix/glue/derivationStrict): support __structuredAttrs r/7376
This adds support to handle the __structuredAttrs argument, which can be passed to builtins.derivationStrict. If __structuredAttrs is passed, and set to true, most of the arguments passed to builtins.derivationStrict are not simply coerced to a string and passed down to "environments", but instead kept in a more structured fashion. Inside ATerm, which is what's relevant as far as path calculation is concerned, a virtual `__json` environment variable is present, containing these structured values. Inside Builds, these structured values are not made available as an environment variable, but a JSON file (and source-able bash script). This will need to be respected once we start emitting BuildRequests, and for that we can probably just parse the `__json` key in Derivation.environment again - or keep this additionally in non-serialized form around during Evaluation. No matter what, this is left for a followup CL. The existing handle_derivation_parameters and populate_outputs helper function were removed, as __structuredAttrs causes quite a change in behaviour, and so handling both in the same place makes it more readable. There's some open questions w.r.t. string contexts for structured attrs itself. A TODO is left for this, but at least path calculation for individual structured attrs derivations are correct now. Part of b/366. Change-Id: Ic293822266ced6f8c4826d8ef0d2e098a4adccaa Reviewed-on: https://cl.tvl.fyi/c/depot/+/10604 Tested-by: BuildkiteCI Reviewed-by: raitobezarius <tvl@lahfa.xyz>
Diffstat (limited to 'tvix')
-rw-r--r-- | tvix/Cargo.lock | 23 | ||||
-rw-r--r-- | tvix/Cargo.nix | 50 | ||||
-rw-r--r-- | tvix/glue/Cargo.toml | 5 | ||||
-rw-r--r-- | tvix/glue/src/builtins/derivation.rs | 359 | ||||
-rw-r--r-- | tvix/glue/src/builtins/mod.rs | 24 | ||||
-rw-r--r-- | tvix/glue/src/tvix_build.rs | 3 |
6 files changed, 267 insertions, 197 deletions
diff --git a/tvix/Cargo.lock b/tvix/Cargo.lock index 9c334df25164..4ce2993cb5c6 100644 --- a/tvix/Cargo.lock +++ b/tvix/Cargo.lock @@ -307,9 +307,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.6.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" dependencies = [ "memchr", "regex-automata", @@ -1458,9 +1458,9 @@ checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memoffset" @@ -2206,9 +2206,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" [[package]] name = "regex-syntax" @@ -2498,18 +2498,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.162" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.162" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2 1.0.75", "quote 1.0.35", @@ -3402,11 +3402,14 @@ dependencies = [ name = "tvix-glue" version = "0.1.0" dependencies = [ + "bstr", "bytes", "criterion", "data-encoding", "lazy_static", "nix-compat", + "serde", + "serde_json", "sha2", "tempfile", "test-case", diff --git a/tvix/Cargo.nix b/tvix/Cargo.nix index 79c2c55ef292..d3947281dcd0 100644 --- a/tvix/Cargo.nix +++ b/tvix/Cargo.nix @@ -1000,9 +1000,9 @@ rec { }; "bstr" = rec { crateName = "bstr"; - version = "1.6.0"; + version = "1.9.0"; edition = "2021"; - sha256 = "01bvsr3x9n75klbwxym0zf939vzim0plsmy786p0zzzvrj6i9637"; + sha256 = "1p6hzf3wqwwynv6w4pn17jg21amfafph9kb5sfvf1idlli8h13y4"; authors = [ "Andrew Gallant <jamslam@gmail.com>" ]; @@ -1027,7 +1027,7 @@ rec { } ]; features = { - "alloc" = [ "serde?/alloc" ]; + "alloc" = [ "memchr/alloc" "serde?/alloc" ]; "default" = [ "std" "unicode" ]; "serde" = [ "dep:serde" ]; "std" = [ "alloc" "memchr/std" "serde?/std" ]; @@ -4315,9 +4315,9 @@ rec { }; "memchr" = rec { crateName = "memchr"; - version = "2.5.0"; - edition = "2018"; - sha256 = "0vanfk5mzs1g1syqnj03q8n0syggnhn55dq535h2wxr7rwpfbzrd"; + version = "2.7.1"; + edition = "2021"; + sha256 = "0jf1kicqa4vs9lyzj4v4y1p90q0dh87hvhsdd5xvhnp527sw8gaj"; authors = [ "Andrew Gallant <jamslam@gmail.com>" "bluss" @@ -4326,11 +4326,12 @@ rec { "compiler_builtins" = [ "dep:compiler_builtins" ]; "core" = [ "dep:core" ]; "default" = [ "std" ]; - "libc" = [ "dep:libc" ]; + "logging" = [ "dep:log" ]; "rustc-dep-of-std" = [ "core" "compiler_builtins" ]; + "std" = [ "alloc" ]; "use_std" = [ "std" ]; }; - resolvedDefaultFeatures = [ "default" "std" ]; + resolvedDefaultFeatures = [ "alloc" "default" "std" ]; }; "memoffset 0.6.5" = rec { crateName = "memoffset"; @@ -6533,9 +6534,9 @@ rec { }; "regex-automata" = rec { crateName = "regex-automata"; - version = "0.3.4"; + version = "0.4.3"; edition = "2021"; - sha256 = "156jmvsbzd9arih42ninzkfgv7g93g6i2fdxc5gki53m1ccxddmp"; + sha256 = "0gs8q9yhd3kcg4pr00ag4viqxnh5l7jpyb9fsfr8hzh451w4r02z"; authors = [ "The Rust Project Developers" "Andrew Gallant <jamslam@gmail.com>" @@ -6548,7 +6549,7 @@ rec { "hybrid" = [ "alloc" "nfa-thompson" ]; "internal-instrument" = [ "internal-instrument-pikevm" ]; "internal-instrument-pikevm" = [ "logging" "std" ]; - "logging" = [ "dep:log" "aho-corasick?/logging" ]; + "logging" = [ "dep:log" "aho-corasick?/logging" "memchr?/logging" ]; "meta" = [ "syntax" "nfa-pikevm" ]; "nfa" = [ "nfa-thompson" "nfa-pikevm" "nfa-backtrack" ]; "nfa-backtrack" = [ "nfa-thompson" ]; @@ -7685,9 +7686,9 @@ rec { }; "serde" = rec { crateName = "serde"; - version = "1.0.162"; - edition = "2015"; - sha256 = "1dksgs0zi9wdh3bm3gzzsvmgg39fn8vb4d8gbz09haswmghzdcki"; + version = "1.0.195"; + edition = "2018"; + sha256 = "00kbc86kgaihpza0zdglcd2qq5468yg0dvvdmkli2y660bs1s9k3"; authors = [ "Erick Tryzelaar <erick.tryzelaar@gmail.com>" "David Tolnay <dtolnay@gmail.com>" @@ -7698,6 +7699,11 @@ rec { packageId = "serde_derive"; optional = true; } + { + name = "serde_derive"; + packageId = "serde_derive"; + target = { target, features }: false; + } ]; devDependencies = [ { @@ -7714,9 +7720,9 @@ rec { }; "serde_derive" = rec { crateName = "serde_derive"; - version = "1.0.162"; + version = "1.0.195"; edition = "2015"; - sha256 = "1diwx4c86b63mgmzbd5nvj8imjwhipm48jlhi62bar7xa91q3852"; + sha256 = "0b7ag1qm9q3fgwlmyk2ap5gjbqa9vyf2wfmj4xish6yq0f38zzj6"; procMacro = true; authors = [ "Erick Tryzelaar <erick.tryzelaar@gmail.com>" @@ -10713,6 +10719,10 @@ rec { else ./glue; dependencies = [ { + name = "bstr"; + packageId = "bstr"; + } + { name = "bytes"; packageId = "bytes"; } @@ -10725,6 +10735,14 @@ rec { packageId = "nix-compat"; } { + name = "serde"; + packageId = "serde"; + } + { + name = "serde_json"; + packageId = "serde_json"; + } + { name = "sha2"; packageId = "sha2"; } diff --git a/tvix/glue/Cargo.toml b/tvix/glue/Cargo.toml index ad776a48f730..c58c66dd60a1 100644 --- a/tvix/glue/Cargo.toml +++ b/tvix/glue/Cargo.toml @@ -4,16 +4,19 @@ version = "0.1.0" edition = "2021" [dependencies] +bstr = "1.6.0" +bytes = "1.4.0" data-encoding = "2.3.3" nix-compat = { path = "../nix-compat" } tvix-build = { path = "../build", default-features = false, features = []} tvix-eval = { path = "../eval" } tvix-castore = { path = "../castore" } tvix-store = { path = "../store", default-features = false, features = []} -bytes = "1.4.0" tracing = "0.1.37" tokio = "1.28.0" thiserror = "1.0.38" +serde = "1.0.195" +serde_json = "1.0" sha2 = "0.10.8" [dependencies.wu-manber] diff --git a/tvix/glue/src/builtins/derivation.rs b/tvix/glue/src/builtins/derivation.rs index 517ea0032180..25b54805e6a4 100644 --- a/tvix/glue/src/builtins/derivation.rs +++ b/tvix/glue/src/builtins/derivation.rs @@ -1,6 +1,7 @@ //! Implements `builtins.derivation`, the core of what makes Nix build packages. use crate::builtins::DerivationError; use crate::known_paths::KnownPaths; +use bstr::BString; use nix_compat::derivation::{Derivation, Output}; use nix_compat::nixhash; use std::cell::RefCell; @@ -10,42 +11,13 @@ use tvix_eval::builtin_macros::builtins; use tvix_eval::generators::{self, emit_warning_kind, GenCo}; use tvix_eval::{ AddContext, CatchableErrorKind, CoercionKind, ErrorKind, NixAttrs, NixContext, - NixContextElement, NixList, Value, WarningKind, + NixContextElement, Value, WarningKind, }; // Constants used for strangely named fields in derivation inputs. const STRUCTURED_ATTRS: &str = "__structuredAttrs"; const IGNORE_NULLS: &str = "__ignoreNulls"; -/// Helper function for populating the `drv.outputs` field from a -/// manually specified set of outputs, instead of the default -/// `outputs`. -async fn populate_outputs( - co: &GenCo, - drv: &mut Derivation, - outputs: NixList, -) -> Result<(), ErrorKind> { - // Remove the original default `out` output. - drv.outputs.clear(); - - for output in outputs { - let output_name = generators::request_force(co, output) - .await - .to_str() - .context("determining output name")?; - - if drv - .outputs - .insert(output_name.as_str().into(), Default::default()) - .is_some() - { - return Err(DerivationError::DuplicateOutput(output_name.as_str().into()).into()); - } - } - - Ok(()) -} - /// Populate the inputs of a derivation from the build references /// found when scanning the derivation's parameters and extracting their contexts. fn populate_inputs(drv: &mut Derivation, full_context: NixContext) { @@ -149,78 +121,10 @@ fn handle_fixed_output( Ok(None) } -/// Handles derivation parameters which are not just forwarded to -/// the environment. The return value indicates whether the -/// parameter should be included in the environment. -async fn handle_derivation_parameters( - drv: &mut Derivation, - co: &GenCo, - name: &str, - value: &Value, - val_str: &str, -) -> Result<Result<bool, CatchableErrorKind>, ErrorKind> { - match name { - IGNORE_NULLS => return Ok(Ok(false)), - - // Command line arguments to the builder. - "args" => { - let args = value.to_list()?; - for arg in args { - match strong_importing_coerce_to_string(co, arg).await? { - Err(cek) => return Ok(Err(cek)), - Ok(s) => drv.arguments.push(s), - } - } - - // The arguments do not appear in the environment. - return Ok(Ok(false)); - } - - // Explicitly specified drv outputs (instead of default [ "out" ]) - "outputs" => { - let outputs = value - .to_list() - .context("looking at the `outputs` parameter of the derivation")?; - - populate_outputs(co, drv, outputs).await?; - } - - "builder" => { - drv.builder = val_str.to_string(); - } - - "system" => { - drv.system = val_str.to_string(); - } - - _ => {} - } - - Ok(Ok(true)) -} - -async fn strong_importing_coerce_to_string( - co: &GenCo, - val: Value, -) -> Result<Result<String, CatchableErrorKind>, ErrorKind> { - let val = generators::request_force(co, val).await; - match generators::request_string_coerce( - co, - val, - CoercionKind { - strong: true, - import_paths: true, - }, - ) - .await - { - Err(cek) => Ok(Err(cek)), - Ok(val_str) => Ok(Ok(val_str.as_str().to_string())), - } -} - #[builtins(state = "Rc<RefCell<KnownPaths>>")] pub(crate) mod derivation_builtins { + use std::collections::BTreeMap; + use super::*; use nix_compat::store_path::hash_placeholder; use tvix_eval::generators::Gen; @@ -258,14 +162,38 @@ pub(crate) mod derivation_builtins { return Err(ErrorKind::Abort("derivation has empty name".to_string())); } - // Check whether attributes should be passed as a JSON file. - // TODO: the JSON serialisation has to happen here. - if let Some(sa) = input.select(STRUCTURED_ATTRS) { - if generators::request_force(&co, sa.clone()).await.as_bool()? { - return Ok(Value::Catchable(CatchableErrorKind::UnimplementedFeature( - STRUCTURED_ATTRS.to_string(), - ))); + let mut drv = Derivation::default(); + drv.outputs.insert("out".to_string(), Default::default()); + let mut input_context = NixContext::new(); + + #[inline] + async fn strong_importing_coerce_to_string( + co: &GenCo, + val: Value, + ) -> Result<NixString, CatchableErrorKind> { + let val = generators::request_force(co, val).await; + match generators::request_string_coerce( + co, + val, + CoercionKind { + strong: true, + import_paths: true, + }, + ) + .await + { + Err(cek) => Err(cek), + Ok(val_str) => Ok(val_str), + } + } + + /// Inserts a key and value into the drv.environment BTreeMap, and fails if the + /// key did already exist before. + fn insert_env(drv: &mut Derivation, k: &str, v: BString) -> Result<(), DerivationError> { + if drv.environment.insert(k.into(), v).is_some() { + return Err(DerivationError::DuplicateEnvVar(k.into())); } + Ok(()) } // Check whether null attributes should be ignored or passed through. @@ -274,82 +202,166 @@ pub(crate) mod derivation_builtins { None => false, }; - let mut drv = Derivation::default(); - drv.outputs.insert("out".to_string(), Default::default()); - - async fn select_string( - co: &GenCo, - attrs: &NixAttrs, - key: &str, - ) -> Result<Result<Option<String>, CatchableErrorKind>, ErrorKind> { - if let Some(attr) = attrs.select(key) { - match strong_importing_coerce_to_string(co, attr.clone()).await? { - Err(cek) => return Ok(Err(cek)), - Ok(str) => return Ok(Ok(Some(str))), - } - } - - Ok(Ok(None)) - } + // peek at the STRUCTURED_ATTRS argument. + // If it's set and true, provide a BTreeMap that gets populated while looking at the arguments. + // We need it to be a BTreeMap, so iteration order of keys is reproducible. + let mut structured_attrs: Option<BTreeMap<String, serde_json::Value>> = + match input.select(STRUCTURED_ATTRS) { + Some(b) => generators::request_force(&co, b.clone()) + .await + .as_bool()? + .then_some(Default::default()), + None => None, + }; - let mut input_context = NixContext::new(); + // Look at the arguments passed to builtins.derivationStrict. + // Some set special fields in the Derivation struct, some change + // behaviour of other functionality. + for (arg_name, arg_value) in input.clone().into_iter_sorted() { + // force the current value. + let value = generators::request_force(&co, arg_value).await; - for (name, value) in input.clone().into_iter_sorted() { - let value = generators::request_force(&co, value).await; + // filter out nulls if ignore_nulls is set. if ignore_nulls && matches!(value, Value::Null) { continue; } - match generators::request_string_coerce( - &co, - value.clone(), - CoercionKind { - strong: true, - import_paths: true, - }, - ) - .await - { - Err(cek) => return Ok(Value::Catchable(cek)), - Ok(val_str) => { - // Learn about this derivation references - // by looking at its context. - input_context.mimic(&val_str); - - let val_str = val_str.as_str().to_string(); - // handle_derivation_parameters tells us whether the - // argument should be added to the environment; continue - // to the next one otherwise - match handle_derivation_parameters( - &mut drv, - &co, - name.as_str(), - &value, - &val_str, - ) - .await? - { + match arg_name.as_str() { + // Command line arguments to the builder. + // These are only set in drv.arguments. + "args" => { + for arg in value.to_list()? { + match strong_importing_coerce_to_string(&co, arg).await { + Err(cek) => return Ok(Value::Catchable(cek)), + Ok(s) => { + input_context.mimic(&s); + drv.arguments.push(s.as_str().to_string()) + } + } + } + } + + // If outputs is set, remove the original default `out` output, + // and replace it with the list of outputs. + "outputs" => { + let outputs = value + .to_list() + .context("looking at the `outputs` parameter of the derivation")?; + + // Remove the original default `out` output. + drv.outputs.clear(); + + let mut output_names = vec![]; + + for output in outputs { + let output_name = generators::request_force(&co, output) + .await + .to_str() + .context("determining output name")?; + + input_context.mimic(&output_name); + + // Populate drv.outputs + if drv + .outputs + .insert(output_name.as_str().to_string(), Default::default()) + .is_some() + { + Err(DerivationError::DuplicateOutput( + output_name.as_str().into(), + ))? + } + output_names.push(output_name.as_str().to_string()); + } + + // Add drv.environment[outputs] unconditionally. + insert_env(&mut drv, arg_name.as_str(), output_names.join(" ").into())?; + // drv.environment[$output_name] is added after the loop, + // with whatever is in drv.outputs[$output_name]. + } + + // handle builder and system. + "builder" | "system" => { + match strong_importing_coerce_to_string(&co, value).await { Err(cek) => return Ok(Value::Catchable(cek)), - Ok(false) => continue, - _ => (), + Ok(val_str) => { + input_context.mimic(&val_str); + + if arg_name.as_str() == "builder" { + drv.builder = val_str.as_str().to_owned(); + } else { + drv.system = val_str.as_str().to_owned(); + } + + // Either populate drv.environment or structured_attrs. + if let Some(ref mut structured_attrs) = structured_attrs { + // No need to check for dups, we only iterate over every attribute name once + structured_attrs + .insert(arg_name.as_str().into(), val_str.as_str().into()); + } else { + insert_env(&mut drv, arg_name.as_str(), val_str.as_bytes().into())?; + } + } } + } - // Most of these are also added to the builder's environment in "raw" form. - if drv - .environment - .insert(name.as_str().to_string(), val_str.into()) - .is_some() - { - return Err( - DerivationError::DuplicateEnvVar(name.as_str().to_string()).into() - ); + // Don't add STRUCTURED_ATTRS if enabled. + STRUCTURED_ATTRS if structured_attrs.is_some() => continue, + // IGNORE_NULLS is always skipped, even if it's not set to true. + IGNORE_NULLS => continue, + + // all other args. + _ => { + // In SA case, force and add to structured attrs. + // In non-SA case, coerce to string and add to env. + if let Some(ref mut structured_attrs) = structured_attrs { + let val = generators::request_force(&co, value).await; + if matches!(val, Value::Catchable(_)) { + return Ok(val); + } + + // TODO(raitobezarius): context for json values? + // input_context.mimic(&val); + + let val_json = match val.into_json(&co).await? { + Ok(v) => v, + Err(cek) => return Ok(Value::Catchable(cek)), + }; + + // No need to check for dups, we only iterate over every attribute name once + structured_attrs.insert(arg_name.as_str().to_string(), val_json); + } else { + match strong_importing_coerce_to_string(&co, value).await { + Err(cek) => return Ok(Value::Catchable(cek)), + Ok(val_str) => { + input_context.mimic(&val_str); + + insert_env(&mut drv, arg_name.as_str(), val_str.as_bytes().into())?; + } + } } } } } + // end of per-argument loop // Configure fixed-output derivations if required. { + async fn select_string( + co: &GenCo, + attrs: &NixAttrs, + key: &str, + ) -> Result<Result<Option<String>, CatchableErrorKind>, ErrorKind> { + if let Some(attr) = attrs.select(key) { + match strong_importing_coerce_to_string(co, attr.clone()).await { + Err(cek) => return Ok(Err(cek)), + Ok(str) => return Ok(Ok(Some(str.as_str().to_string()))), + } + } + + Ok(Ok(None)) + } + let output_hash = match select_string(&co, &input, "outputHash") .await .context("evaluating the `outputHash` parameter")? @@ -380,8 +392,9 @@ pub(crate) mod derivation_builtins { } // Each output name needs to exist in the environment, at this - // point initialised as an empty string because that is the - // way of Golang ;) + // point initialised as an empty string, as the ATerm serialization of that is later + // used for the output path calculation (which will also update output + // paths post-calculation, both in drv.environment and drv.outputs) for output in drv.outputs.keys() { if drv .environment @@ -392,6 +405,14 @@ pub(crate) mod derivation_builtins { } } + if let Some(structured_attrs) = structured_attrs { + // configure __json + drv.environment.insert( + "__json".to_string(), + BString::from(serde_json::to_string(&structured_attrs)?), + ); + } + populate_inputs(&mut drv, input_context); let mut known_paths = state.borrow_mut(); diff --git a/tvix/glue/src/builtins/mod.rs b/tvix/glue/src/builtins/mod.rs index 497688cab564..d5d42bcec911 100644 --- a/tvix/glue/src/builtins/mod.rs +++ b/tvix/glue/src/builtins/mod.rs @@ -119,6 +119,30 @@ mod tests { }).outPath "#, "/nix/store/5vyvcwah9l9kf07d52rcgdk70g2f4y13-foo"; "full")] #[test_case(r#"(builtins.derivation { "name" = "foo"; passAsFile = ["bar"]; bar = "baz"; system = ":"; builder = ":";}).outPath"#, "/nix/store/25gf0r1ikgmh4vchrn8qlc4fnqlsa5a1-foo"; "passAsFile")] + // __ignoreNulls = true, but nothing set to null + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = true; }).drvPath"#, "/nix/store/xa96w6d7fxrlkk60z1fmx2ffp2wzmbqx-foo.drv"; "ignoreNulls no arg drvPath")] + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = true; }).outPath"#, "/nix/store/pk2agn9za8r9bxsflgh1y7fyyrmwcqkn-foo"; "ignoreNulls no arg outPath")] + // __ignoreNulls = true, with a null arg, same paths + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = true; ignoreme = null; }).drvPath"#, "/nix/store/xa96w6d7fxrlkk60z1fmx2ffp2wzmbqx-foo.drv"; "ignoreNulls drvPath")] + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = true; ignoreme = null; }).outPath"#, "/nix/store/pk2agn9za8r9bxsflgh1y7fyyrmwcqkn-foo"; "ignoreNulls outPath")] + // __ignoreNulls = false + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = false; }).drvPath"#, "/nix/store/xa96w6d7fxrlkk60z1fmx2ffp2wzmbqx-foo.drv"; "ignoreNulls false no arg drvPath")] + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = false; }).outPath"#, "/nix/store/pk2agn9za8r9bxsflgh1y7fyyrmwcqkn-foo"; "ignoreNulls false no arg arg outPath")] + // __ignoreNulls = false, with a null arg + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = false; foo = null; }).drvPath"#, "/nix/store/xwkwbajfiyhdqmksrbzm0s4g4ib8d4ms-foo.drv"; "ignoreNulls false arg drvPath")] + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = false; foo = null; }).outPath"#, "/nix/store/2n2jqm6l7r2ahi19m58pl896ipx9cyx6-foo"; "ignoreNulls false arg arg outPath")] + // structured attrs set to false will render an empty string inside env + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = false; foo = "bar"; }).drvPath"#, "/nix/store/qs39krwr2lsw6ac910vqx4pnk6m63333-foo.drv"; "structuredAttrs-false-drvPath")] + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = false; foo = "bar"; }).outPath"#, "/nix/store/9yy3764rdip3fbm8ckaw4j9y7vh4d231-foo"; "structuredAttrs-false-outPath")] + // simple structured attrs + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = true; foo = "bar"; }).drvPath"#, "/nix/store/k6rlb4k10cb9iay283037ml1nv3xma2f-foo.drv"; "structuredAttrs-simple-drvPath")] + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = true; foo = "bar"; }).outPath"#, "/nix/store/6lmv3hyha1g4cb426iwjyifd7nrdv1xn-foo"; "structuredAttrs-simple-outPath")] + // structured attrs with outputsCheck + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = true; foo = "bar"; outputChecks = {out = {maxClosureSize = 256 * 1024 * 1024; disallowedRequisites = [ "dev" ];};}; }).drvPath"#, "/nix/store/fx9qzpchh5wchchhy39bwsml978d6wp1-foo.drv"; "structuredAttrs-outputChecks-drvPath")] + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __structuredAttrs = true; foo = "bar"; outputChecks = {out = {maxClosureSize = 256 * 1024 * 1024; disallowedRequisites = [ "dev" ];};}; }).outPath"#, "/nix/store/pcywah1nwym69rzqdvpp03sphfjgyw1l-foo"; "structuredAttrs-outputChecks-outPath")] + // structured attrs and __ignoreNulls. ignoreNulls is inactive (so foo ends up in __json, yet __ignoreNulls itself is not present. + #[test_case(r#"(builtins.derivation { name = "foo"; system = ":"; builder = ":"; __ignoreNulls = false; foo = null; __structuredAttrs = true; }).drvPath"#, "/nix/store/rldskjdcwa3p7x5bqy3r217va1jsbjsc-foo.drv"; "structuredAttrs-and-ignore-nulls-drvPath")] + fn test_outpath(code: &str, expected_path: &str) { let value = eval(code).value.expect("must succeed"); diff --git a/tvix/glue/src/tvix_build.rs b/tvix/glue/src/tvix_build.rs index 227acd252cc4..e8dab1056807 100644 --- a/tvix/glue/src/tvix_build.rs +++ b/tvix/glue/src/tvix_build.rs @@ -87,7 +87,8 @@ where ); handle_pass_as_file(&mut environment_vars, &mut additional_files)?; - // TODO: handle structuredAttrs. + + // TODO: handle __json (structured attrs, provide JSON file and source-able bash script) // Produce inputs. As we refer to the contents here, not just plain store path strings, // we need to perform lookups. |