ensc-build-rs 0.1.3

Utilities for build.rs
Documentation
use std::{io::Write, path::PathBuf};

trait TrimmedRead {
    fn read_line_trimmed(&mut self, buf: &mut String) -> std::io::Result<bool>;
}

impl<R: std::io::Read> TrimmedRead for std::io::BufReader<R> {
    fn read_line_trimmed(&mut self, buf: &mut String) -> std::io::Result<bool>
    {
	use std::io::BufRead;

	match self.read_line(buf)? {
	    0	=> Ok(false),
	    _	=> {
		*buf = buf.trim().into();
		Ok(true)
	    }
	}
    }
}

pub struct LineIterator<R: std::io::Read> {
    reader:	std::io::BufReader<R>,
}

impl <R: std::io::Read> LineIterator<R> {
    pub fn new(r: R) -> Self {
	Self {
	    reader: std::io::BufReader::new(r),
	}
    }
}

pub trait ToLineIterator<R>
where
    R: std::io::Read,
{
    fn to_line_iterator(&mut self) -> LineIterator<R>;
}

impl <R> ToLineIterator<R> for Option<R>
where
    R: std::io::Read,
{
    fn to_line_iterator(&mut self) -> LineIterator<R>
    {
	LineIterator::new(self.take().unwrap())
    }
}

impl <R: std::io::Read> From<R> for LineIterator<R> {
    fn from(value: R) -> Self {
        LineIterator::new(value)
    }
}

impl <R: std::io::Read> std::iter::Iterator for LineIterator<R> {
    type Item	= std::io::Result<String>;

    fn next(&mut self) -> Option<Self::Item> {
	let mut line = String::new();

	match self.reader.read_line_trimmed(&mut line) {
	    Ok(true)	=> Some(Ok(line)),
	    Ok(false)	=> None,
	    Err(e)	=> Some(Err(e)),
	}
    }
}

pub fn finish_cmd<E>(st: Result<std::process::ExitStatus,E>)
where
    E: std::fmt::Debug
{
    match st {
        Err(e) => {
            panic!("make aborted with {:?}", e);
        }

        Ok(s) => {
            if !s.success() {
                panic!("make failed with {:?}", s);
            }
        }
    }
}

fn makedir() -> PathBuf {
    let outdir: PathBuf = std::env::var("OUT_DIR").unwrap().into();

    outdir.join("ensc-build-rs")
}

pub fn run_make() -> std::process::Command {
    let mut res = std::process::Command::new("make");

    res.args(["-s", "--no-print-directory"])
        .arg("--include-dir")
        .arg(std::env::var("OUT_DIR").unwrap())
        .arg("IN_ENSC_BUILD_RS=1");

//    std::thread::sleep(std::time::Duration::from_secs(10));

    res
}

pub fn write_common_mk() {
    static COMMON_MK: &[u8] = include_bytes!("../mk/common.mk");
    let makedir = makedir();

    // ignore errors when creating the directory; it is very likely that we
    // get EEXIST.  Other errors will be catched below when creating the file
    let _ = std::fs::create_dir(&makedir);

    let mut file = std::fs::File::create(makedir.join("common.mk")).unwrap();

    file.write_all(COMMON_MK).unwrap();
    file.flush().unwrap();
}

pub fn emit_libs() {
    let mut cmd = run_make()
	.arg("emit-libs")
	.stdout(std::process::Stdio::piped())
	.spawn()
	.unwrap();

    cmd.stdout
	.to_line_iterator()
	.map(|v| v.unwrap())
	.filter(|l| !l.is_empty())
        .for_each(|l| println!("cargo:rustc-flags={l}"));

    finish_cmd(cmd.wait());
}

pub fn get_cflags() -> Vec<String> {
    let mut cmd = run_make()
	.arg("emit-cflags")
	.stdout(std::process::Stdio::piped())
	.spawn()
	.unwrap();

    let res = cmd.stdout
	.to_line_iterator()
	.map(|v| v.unwrap())
	.filter(|l| !l.is_empty())
	.collect();

    finish_cmd(cmd.wait());

    res
}