1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

use std::process::Output;
use std::sync::Once;
use camino::Utf8PathBuf as PathBuf;

use crate::{cmd, Result};


// FIXME: Could merge this with Container, but not worth it ATM.
pub fn build_in_container(target_ext: &str, projdir: &str, features: &str, rust_ver: &str) -> Result<Output> {
    let build_image = format!("docker.io/rust:{rust_ver}-slim-bullseye");

    let builddir = "/opt/src";
    let target_base = format!("{builddir}/target");
    let imgtarget = format!("{target_base}/{target_ext}");
    let volume = format!("{projdir}:{builddir}");
    let cargo_env = format!("CARGO_HOME={target_base}/.cargo");

    let cargo_cli = vec!["cargo", "build", "--release",
                         "--features", features,
                         "--target-dir", imgtarget.as_str()];

    let docker_cli = vec!["run", "--rm",
                          "--volume", volume.as_str(),
                          "--workdir", builddir,
                          "--env", cargo_env.as_str(),
                          build_image.as_str()];

    let out = cmd([docker_cli, cargo_cli].concat())?;

    Ok(out)
}


static BUILD_LOCK: Once = Once::new();

// Build a project in a rust container. Uses locking to ensure
// concurrent test runs share a common build.
pub fn build_target(bin_name: &str, projdir: &str, features: Option<&str>, rust_ver: &str) -> Result<PathBuf> {
    let ext_base = "docker";
    let fstr = features.unwrap_or("");
    let target_ext = format!("{ext_base}/{}", fstr.replace(" ", "_"));

    BUILD_LOCK.call_once(|| { build_in_container(&target_ext, projdir, fstr, rust_ver).unwrap(); } );

    let bin = PathBuf::from(format!("{projdir}/target/{target_ext}/release/{bin_name}"));
    Ok(bin)
}