Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ every derivation in it gets built. So `iets build .#checks` builds all the
checks. The walk is `nix-build`'s: names in order, a nested set only with
`recurseForDerivations = true`, and a list always.

Strings returned by `builtins.outputOf` can be built directly, including
nested dynamic derivations. Each target keeps its own output selection.
The worker-protocol builder resolves the chain at build time; evaluation
does not build anything, and `--no-build` only uploads the concrete
producer's closure. `--dry-run` asks the daemon without realising the
chain. The same strings work with the modern `nix build` compatibility
command. The gRPC transport does not support dynamic installables.

In a set that has a member named after your system, iets goes into that
member first. That is how a flake lays out `checks`,
`packages` and `devShells`. To build for another system, name it in full:
Expand Down
16 changes: 8 additions & 8 deletions examples/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ use std::collections::HashMap;
use std::env;
use std::process::{self, ExitCode};

use iets::api::installable::drv_roots;
use iets::api::installable::derived_roots;
use iets::api::{self, EvalArgs, Input};
use iets::build::Dispatcher;
use iets::build::render::Style;
use iets::build::upload::PlanUpload;
use iets::build::{Dispatcher, Target};
use iets::eval::Evaluator;
use iets::remote::{plan, worker};
use iets::store::locate::locate_in_store;
Expand Down Expand Up @@ -61,12 +61,13 @@ fn main() -> ExitCode {

fn eval_and_build(ev: &mut Evaluator, a: EvalArgs, url: String) -> u8 {
let mut roots = Vec::new();
if let Err(e) = api::evaluate(ev, a).and_then(|v| drv_roots(ev, v, &mut roots)) {
if let Err(e) = api::evaluate(ev, a).and_then(|v| derived_roots(ev, v, &mut roots)) {
api::report(ev, &e);
return 1;
}
// `--out` would add wanted outputs; the roots' string context is enough here.
let (root_paths, outputs) = api::build_targets(&[], &roots);
let mut targets = api::build_targets(&[], &roots);
let root_paths: Vec<_> = targets.iter().map(|t| t.drv.root.clone()).collect();
let prov = iets::provenance::Provenance::snapshot(ev);

// Every derivation and source this evaluation created, straight from
Expand Down Expand Up @@ -105,10 +106,9 @@ fn eval_and_build(ev: &mut Evaluator, a: EvalArgs, url: String) -> u8 {
return 1;
}
};
let targets: Vec<Target> = root_paths
.iter()
.map(|r| Target::new(r.name(), r.clone(), outputs.clone()))
.collect();
for t in &mut targets {
t.attr = t.drv.name().to_string();
}
let outcomes = match d.submit(&mut PlanUpload::new(&plan, &locate), targets) {
Ok(o) => o,
Err(e) => {
Expand Down
29 changes: 15 additions & 14 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub mod testkit;
pub mod value;
pub mod watch;

use crate::api::build::DerivedPath;
use crate::build::Target;
use crate::compile::cache::IrCache;
use crate::env::Env;
use crate::eval::{self, Error, ErrorKind, EvalConfig, Evaluator, Pos, search_path};
Expand All @@ -29,7 +31,6 @@ use crate::provenance::{describe_pos, rewrite_msg};
use crate::runtime::Word;
use crate::session::Session;
use crate::source::nix_conf;
use crate::store::path::StorePath;
use crate::sys::{self, OsStringExt};
use crate::trace::key::{Key, has_relative_path};
use crate::trace::run::Cache;
Expand Down Expand Up @@ -438,19 +439,19 @@ pub fn full_includes(includes: &[(Vec<u8>, Vec<u8>)]) -> Vec<(Vec<u8>, Vec<u8>)>

/// The derivations to build and the outputs to print: `wanted` plus the
/// outputs the roots' string context named.
pub fn build_targets(
wanted: &[String],
roots: &[(StorePath, Option<String>)],
) -> (Vec<StorePath>, Vec<String>) {
let mut outputs = wanted.to_vec();
for (_, out) in roots {
if let Some(o) = out
&& !outputs.contains(o)
{
outputs.push(o.clone());
}
}
(roots.iter().map(|(p, _)| p.clone()).collect(), outputs)
pub fn build_targets(wanted: &[String], roots: &[(DerivedPath, Option<String>)]) -> Vec<Target> {
roots
.iter()
.map(|(drv, output)| {
let mut outputs = wanted.to_vec();
if let Some(output) = output
&& !outputs.contains(output)
{
outputs.push(output.clone());
}
Target::new("", drv.clone(), outputs)
})
.collect()
}

/// The tracing cache for this run, keyed on the eval arguments. `None` when the
Expand Down
5 changes: 3 additions & 2 deletions src/api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use crate::provenance::Provenance;
use crate::remote::plan;
use crate::remote::worker::Missing;
use crate::settings;
pub use crate::store::derived::DerivedPath;
use crate::store::locate::locate_in_store;
use crate::store::path::StorePath;
use crate::store::source::SourceOrigin;
Expand All @@ -34,13 +35,13 @@ use crate::store::drv::Derivation;
/// One derivation and one of its outputs a command asked for.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Root {
pub drv: StorePath,
pub drv: DerivedPath,
pub output: String,
}

impl Root {
pub fn paths(roots: &[Root]) -> Vec<StorePath> {
roots.iter().map(|r| r.drv.clone()).collect()
roots.iter().map(|r| r.drv.root.clone()).collect()
}
}

Expand Down
74 changes: 58 additions & 16 deletions src/api/installable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use std::collections::HashSet;
use std::str::from_utf8;

use crate::api::build::Root;
use crate::api::build::{DerivedPath, Root};
use crate::builtins::strings::parse_drv_name_bytes;
use crate::eval::{Error, ErrorKind, EvalResult, Evaluator, Pos};
use crate::runtime::{self, AttrsObj, Cell, Ctx, Word};
Expand Down Expand Up @@ -379,6 +379,26 @@ pub fn drv_roots(
ev: &mut Evaluator,
v: Word,
roots: &mut Vec<(StorePath, Option<String>)>,
) -> EvalResult<()> {
let mut derived = Vec::new();
derived_roots(ev, v, &mut derived)?;
for (drv, output) in derived {
if !drv.chain.is_empty() {
return Err(Error::boxed(
ErrorKind::Unsupported,
"this operation needs a concrete derivation; use build for dynamic outputs",
Pos::NONE,
));
}
roots.push((drv.root, output));
}
Ok(())
}

pub fn derived_roots(
ev: &mut Evaluator,
v: Word,
roots: &mut Vec<(DerivedPath, Option<String>)>,
) -> EvalResult<()> {
let bad = |msg: String| Error::boxed(ErrorKind::Eval, msg, Pos::NONE);
let v = ev.force(v)?;
Expand All @@ -392,7 +412,7 @@ pub fn drv_roots(
if !p.is_derivation() {
return Err(bad(format!("'{p}' is not a derivation path")));
}
roots.push((p, None));
roots.push((p.into(), None));
return Ok(());
}
if let Some(s) = v.as_string() {
Expand All @@ -401,18 +421,20 @@ pub fn drv_roots(
match c {
Ctx::Built { drv, output } => {
let (layers, root) = runtime::split_built(drv);
if !layers.is_empty() {
return Err(bad(
"iets: the output of a dynamic derivation is not an installable"
.to_string(),
));
}
let drv = DerivedPath {
root: parse_drv(root)?,
chain: layers
.into_iter()
.rev()
.map(|n| String::from_utf8_lossy(n).into_owned())
.collect(),
};
let out = String::from_utf8_lossy(output).into_owned();
roots.push((parse_drv(root)?, Some(out)));
roots.push((drv, Some(out)));
found = true;
}
Ctx::DrvDeep(drv) => {
roots.push((parse_drv(drv)?, None));
roots.push((parse_drv(drv)?.into(), None));
found = true;
}
Ctx::Opaque(_) | Ctx::Impure(_) => {}
Expand All @@ -428,7 +450,7 @@ pub fn drv_roots(
}
if let Some(l) = v.as_list() {
for c in l.cells() {
drv_roots(ev, c.get(), roots)?;
derived_roots(ev, c.get(), roots)?;
}
return Ok(());
}
Expand All @@ -446,11 +468,11 @@ pub fn roots_of(
v: Word,
args: Word,
attr: &str,
roots: &mut Vec<(StorePath, Option<String>)>,
roots: &mut Vec<(DerivedPath, Option<String>)>,
) -> EvalResult<()> {
let v = ev.auto_call(v, args)?;
let Some(tree) = v.as_attrs().filter(|a| a.get(ev.s.drv_path).is_none()) else {
return drv_roots(ev, v, roots);
return derived_roots(ev, v, roots);
};
let system = ev.current_system();
let sym = ev.sym(system.as_bytes());
Expand Down Expand Up @@ -570,7 +592,10 @@ fn one_derivation(
}
ev.drv.set_attr(&drv, attr);
if seen.insert((drv.to_string(), output.clone())) {
out.push(Root { drv, output });
out.push(Root {
drv: drv.into(),
output,
});
}
Ok(true)
}
Expand Down Expand Up @@ -675,7 +700,7 @@ fn main_program(ev: &mut Evaluator, attrs: &'static AttrsObj) -> EvalResult<Opti

/// One installable's derivation and the outputs it installs.
pub struct Install {
pub drv: StorePath,
pub drv: DerivedPath,
pub outputs: Vec<String>,
}

Expand Down Expand Up @@ -707,6 +732,23 @@ pub fn to_derivation(ev: &mut Evaluator, v: Word, what: &str) -> EvalResult<Stor
/// the installable in the error a non-derivation gets.
pub fn to_install(ev: &mut Evaluator, v: Word, what: &str, flake: bool) -> EvalResult<Install> {
let v = ev.force(v)?;
if let Some(s) = v.as_string()
&& s.ctx.iter().any(|c| matches!(c, Ctx::Built { .. }))
{
let mut roots = Vec::new();
derived_roots(ev, v, &mut roots)?;
if let [(drv, Some(output))] = roots.as_slice() {
return Ok(Install {
drv: drv.clone(),
outputs: vec![output.clone()],
});
}
return Err(Error::boxed(
ErrorKind::Eval,
"a string installable must name exactly one derivation output",
Pos::NONE,
));
}
let unsupported = |msg: String| Error::boxed(ErrorKind::Unsupported, msg, Pos::NONE);
let not_a_drv = || unsupported(format!("the installable '{what}' is not a derivation"));
let Some(attrs) = v.as_attrs() else {
Expand All @@ -715,7 +757,7 @@ pub fn to_install(ev: &mut Evaluator, v: Word, what: &str, flake: bool) -> EvalR
if !is_derivation(ev, attrs)? {
return Err(not_a_drv());
}
let drv = drv_of(ev, attrs)?;
let drv: DerivedPath = drv_of(ev, attrs)?.into();

let specified_sym = ev.sym(b"outputSpecified");
let specified = match attrs.get(specified_sym) {
Expand Down
6 changes: 3 additions & 3 deletions src/api/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::store::path::StorePath;
use crate::trace::run::Cache;
use crate::trace::store::Part;

use super::build::{self, Plan};
use super::build::{self, DerivedPath, Plan};
use super::{EvalArgs, project_root, report, setup_cache, setup_cache_more};
use crate::error::{self, Error};

Expand All @@ -25,7 +25,7 @@ pub enum Outcome<T> {
}

/// A build entry's result: each root derivation and the outputs wanted of it.
pub type Roots = Vec<(StorePath, Option<String>)>;
pub type Roots = Vec<(DerivedPath, Option<String>)>;

pub struct Run {
/// The evaluator is built from it once, in `go`.
Expand Down Expand Up @@ -112,7 +112,7 @@ impl Run {
pub fn hit_plan(&self) -> Option<(Roots, Plan)> {
self.hit(|c| {
let t = c.read_build()?;
let paths: Vec<StorePath> = t.roots.iter().map(|(p, _)| p.clone()).collect();
let paths: Vec<StorePath> = t.roots.iter().map(|(p, _)| p.root.clone()).collect();
let inputs = build::Inputs {
drvs: &t.drvs,
sources: &t.sources,
Expand Down
Loading
Loading