snix_glue/builder/structured_attrs/mod.rs
1//! Contains code modifying BuildRequest to honor structuredAttrs functionality.
2// NOTE: This is all quite annoying to debug, due to some of the following reasons:
3// - structured attrs only works with a real bash.
4// as `NIX_ATTRS_SH_FILE` uses `declare`, which is not present in the busybox sandbox shell.
5// - We must source the bash script, as neither `$PATH` nor `$out` will be present in the env.
6// If you want to inspect file contents and/or env vars set, the following
7// nix repl prompt might be a good starting point (invoked with `-f path/to/nixpkgs`):
8// ```
9// :b builtins.derivation { name = "script.sh"; system = builtins.currentSystem; PATH = lib.makeBinPath [pkgs.coreutils]; FOO = "bar"; builder="${bash}/bin/bash";
10// args = ["-xc" "source \${NIX_ATTRS_SH_FILE:-/dev/null}; cat \${NIX_ATTRS_JSON_FILE:-/dev/null}; out=\${out:-\${outputs[out]}}; cat \${NIX_ATTRS_SH_FILE:-/dev/null} >\$out; exit 0"];
11// __structuredAttrs = true;}
12// ```
13// NOTE: when inspecting the created env, the _ one comes from bash itself!
14// > When bash invokes an external command, the variable _ is set to the full
15// > pathname of the command and passed to that command in its environment.
16
17use std::collections::BTreeMap;
18
19use bytes::Bytes;
20use nix_compat::store_path::StorePath;
21mod shell;
22
23pub const JSON_KEY: &str = "__json";
24
25pub const SH_FILE_ENV_NAME: &str = "NIX_ATTRS_SH_FILE";
26pub const SH_FILE_PATH: &str = "/build/.attrs.sh";
27pub const JSON_FILE_ENV_NAME: &str = "NIX_ATTRS_JSON_FILE";
28pub const JSON_FILE_PATH: &str = "/build/.attrs.json";
29
30// FUTUREWORK: on other architectures (MacOS) these paths might not be const…
31
32/// handle structuredAttrs.
33/// This takes the contents of the __json env in the Derivation,
34/// and populates `environment_vars` and `additional_files`.
35pub fn handle_structured_attrs<'a>(
36 json_str: &[u8],
37 outputs: impl Iterator<Item = (&'a str, StorePath<&'a str>)>,
38 environment_vars: &mut BTreeMap<String, Vec<u8>>,
39 additional_files: &mut BTreeMap<String, Bytes>,
40) -> std::io::Result<()> {
41 let mut map = match serde_json::from_slice::<serde_json::Value>(json_str) {
42 Ok(serde_json::Value::Object(map)) => map,
43 Ok(_) => panic!("Snix bug: produced non-object at __json key in Derivation"),
44 Err(e) => return Err(e.into()),
45 };
46
47 // Nix adds outputs[output_name] = output_path to the JSON and shell script inside the build (but not in the ATerm)
48 map.insert(
49 "outputs".to_string(),
50 serde_json::Value::Object(serde_json::Map::from_iter(outputs.map(
51 |(output_name, output_path)| {
52 (
53 output_name.to_string(),
54 serde_json::Value::String(output_path.to_absolute_path()),
55 )
56 },
57 ))),
58 );
59
60 environment_vars.insert(JSON_FILE_ENV_NAME.to_owned(), JSON_FILE_PATH.into());
61 additional_files.insert(
62 JSON_FILE_PATH.to_string(),
63 serde_json::to_string(&map)
64 .expect("Snix bug: unable to serialize json")
65 .into(),
66 );
67
68 environment_vars.insert(SH_FILE_ENV_NAME.to_owned(), SH_FILE_PATH.into());
69 additional_files.insert(SH_FILE_PATH.to_string(), {
70 let mut out = String::new();
71 shell::write_attrs_sh_file(&mut out, map).unwrap();
72 Bytes::from(out)
73 });
74
75 Ok(())
76}