criterion-polyglot 0.1.0

An extension trait for criterion providing benchmark methods for various non-Rust programming languages
Documentation
use std::{ffi::OsStr, process::Command, fs};

use tempfile::TempPath;

use crate::{
    helper::{make_tempdir, get_criterion_env_var, make_empty_tempfile, make_spec_tempfile},
    runner::{run_command, CommunicatingBenchmark}, BenchSpec,
};

const ZIG_COMPILER_INPUT_FILE: &str = "bench.zig";
const ZIG_COMPILER_OUTPUT_FILE: &str = "bench";
const HARNESS: &str = include_str!("harness.zig.in");
const SOURCE_EXTENSION: &str = ".zig";

pub fn compile(spec: BenchSpec) -> TempPath {
    let zig_temp_code = make_spec_tempfile(self::SOURCE_EXTENSION, HARNESS, &spec);

    // Zig will only output an executable with the basename of the compiled file,
    // so create a temporary directory and copy the assembled benchmark harness
    // into it with the name "bench.zig" so we know the executable will be called "bench"
    let zig_temp_dir = make_tempdir();
    let zig_temp_dir_code_path = zig_temp_dir.path().join(ZIG_COMPILER_INPUT_FILE);
    let zig_temp_dir_exe_path = zig_temp_dir.path().join(ZIG_COMPILER_OUTPUT_FILE);
    fs::copy(&zig_temp_code, &zig_temp_dir_code_path)
        .expect("couldn't copy Zig code to temporary directory");

    let zig_path = get_criterion_env_var("ZIG", "zig");
    let mut compiler_command = Command::new(zig_path);
    compiler_command
        .current_dir(&zig_temp_dir)
        .arg("build-exe")
        .arg(&zig_temp_dir_code_path);

    run_command("compiling Zig", &mut compiler_command, Some(zig_temp_code));

    // If the compiler created an executable in the expected location, copy
    // it to a new, empty tempfile so it sticks around when the temporary
    // directory is deleted
    let temp_executable = make_empty_tempfile(super::BIN_EXTENSION);
    let copy_result = fs::copy(&zig_temp_dir_exe_path, &temp_executable);
    if let Err(e) = copy_result {
        let zig_temp_dir = zig_temp_dir.into_path();
        panic!(
            "Could not copy Zig executable from expected path `{exe}`: {e}\n\n\
             Temporary directory preserved: `{dir}`",
             exe=zig_temp_dir_exe_path.display(),
             dir=zig_temp_dir.display(),
        );
    }
    temp_executable
}

pub fn spawn(executable: impl AsRef<OsStr>) -> CommunicatingBenchmark {
    let command = Command::new(executable.as_ref());

    CommunicatingBenchmark::start("running Zig", command, None)
}