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}