extern crate alloc;
mod error;
use alloc::sync::Arc;
pub use error::BuildError;
use glob::{Paths, glob};
use stak_compiler::compile_r7rs;
use std::{
env,
ffi::OsStr,
path::{Path, PathBuf},
process::Stdio,
};
use tokio::{
fs::{create_dir_all, read, write},
io::{AsyncReadExt, AsyncWriteExt},
process::Command,
runtime::Runtime,
spawn,
};
use which::which;
pub fn build_r7rs() -> Result<(), BuildError> {
let runtime = Runtime::new()?;
let _ = runtime.enter();
runtime.block_on(build(glob("**/*.scm")?))?;
Ok(())
}
async fn build(paths: Paths) -> Result<(), BuildError> {
let compiler = which("stak-compile").ok().map(Arc::new);
if compiler.is_none() {
println!("cargo::warning={}",
[
"Using an internal compiler for Stak Scheme.",
"It can be very slow unless you modify `profile.<profile>.build-override` in your `Cargo.toml` file to set `opt-level = 3`.",
"For more information, see https://doc.rust-lang.org/cargo/reference/profiles.html#build-dependencies.",
"Also, consider installing the external compiler by running `cargo install stak-compile`.",
].join(" ")
);
}
let out_directory_variable = env::var("OUT_DIR")?;
let out_directory = Path::new(&out_directory_variable);
let mut handles = vec![];
for path in paths {
let path = path?;
if path
.iter()
.any(|component| component == OsStr::new("target"))
{
continue;
}
println!("cargo::rerun-if-changed={}", path.display());
let out_path = out_directory.join(&path);
handles.push(spawn(compile(path, out_path, compiler.clone())))
}
for handle in handles {
handle.await??;
}
Ok(())
}
async fn compile(
src_path: PathBuf,
out_path: PathBuf,
compiler: Option<Arc<PathBuf>>,
) -> Result<(), BuildError> {
let mut buffer = vec![];
if let Some(path) = compiler {
let mut command = Command::new(&*path)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
let stdin = command.stdin.as_mut().expect("stdin");
stdin
.write_all(include_str!("prelude.scm").as_bytes())
.await?;
stdin.write_all(&read(src_path).await?).await?;
command.wait().await?;
command
.stdout
.expect("stdout")
.read_to_end(&mut buffer)
.await?;
} else {
compile_r7rs(&*read(&src_path).await?, &mut buffer)?;
}
if let Some(path) = out_path.parent() {
create_dir_all(path).await?;
}
write(out_path, &buffer).await?;
Ok(())
}