feat(tvix/glue/derivationStrict): support __structuredAttrs
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>
This commit is contained in:
parent
82540717d6
commit
d516ce56b1
6 changed files with 267 additions and 197 deletions
23
tvix/Cargo.lock
generated
23
tvix/Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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>"
|
||||
|
@ -10712,6 +10718,10 @@ rec {
|
|||
then lib.cleanSourceWith { filter = sourceFilter; src = ./glue; }
|
||||
else ./glue;
|
||||
dependencies = [
|
||||
{
|
||||
name = "bstr";
|
||||
packageId = "bstr";
|
||||
}
|
||||
{
|
||||
name = "bytes";
|
||||
packageId = "bytes";
|
||||
|
@ -10724,6 +10734,14 @@ rec {
|
|||
name = "nix-compat";
|
||||
packageId = "nix-compat";
|
||||
}
|
||||
{
|
||||
name = "serde";
|
||||
packageId = "serde";
|
||||
}
|
||||
{
|
||||
name = "serde_json";
|
||||
packageId = "serde_json";
|
||||
}
|
||||
{
|
||||
name = "sha2";
|
||||
packageId = "sha2";
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,98 +162,206 @@ 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.
|
||||
let ignore_nulls = match input.select(IGNORE_NULLS) {
|
||||
Some(b) => generators::request_force(&co, b.clone()).await.as_bool()?,
|
||||
None => false,
|
||||
};
|
||||
|
||||
let mut drv = Derivation::default();
|
||||
drv.outputs.insert("out".to_string(), Default::default());
|
||||
// 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,
|
||||
};
|
||||
|
||||
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))),
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
|
||||
Ok(Ok(None))
|
||||
}
|
||||
|
||||
let mut input_context = NixContext::new();
|
||||
|
||||
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);
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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?
|
||||
{
|
||||
Err(cek) => return Ok(Value::Catchable(cek)),
|
||||
Ok(false) => continue,
|
||||
_ => (),
|
||||
// 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());
|
||||
}
|
||||
|
||||
// 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()
|
||||
);
|
||||
// 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(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())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue