1extern crate alloc;
22
23mod error;
24
25use alloc::sync::Arc;
26pub use error::BuildError;
27use glob::{Paths, glob};
28use stak_compiler::compile_r7rs;
29use std::{
30 env,
31 ffi::OsStr,
32 path::{Path, PathBuf},
33 process::Stdio,
34};
35use tokio::{
36 fs::{create_dir_all, read, write},
37 io::{AsyncReadExt, AsyncWriteExt},
38 process::Command,
39 runtime::Runtime,
40 spawn,
41};
42use which::which;
43
44pub fn build_r7rs() -> Result<(), BuildError> {
50 let runtime = Runtime::new()?;
51 let _ = runtime.enter();
52
53 runtime.block_on(build(glob("**/*.scm")?))?;
54
55 Ok(())
56}
57
58async fn build(paths: Paths) -> Result<(), BuildError> {
59 let compiler = which("stak-compile").ok().map(Arc::new);
60
61 if compiler.is_none() {
62 println!("cargo::warning={}",
63 [
64 "Using an internal compiler for Stak Scheme.",
65 "It can be very slow unless you modify `profile.<profile>.build-override` in your `Cargo.toml` file to set `opt-level = 3`.",
66 "For more information, see https://doc.rust-lang.org/cargo/reference/profiles.html#build-dependencies.",
67 "Also, consider installing the external compiler by running `cargo install stak-compile`.",
68 ].join(" ")
69 );
70 }
71
72 let out_directory_variable = env::var("OUT_DIR")?;
73 let out_directory = Path::new(&out_directory_variable);
74
75 let mut handles = vec![];
76
77 for path in paths {
78 let path = path?;
79
80 if path
81 .iter()
82 .any(|component| component == OsStr::new("target"))
83 {
84 continue;
85 }
86
87 println!("cargo::rerun-if-changed={}", path.display());
88
89 let out_path = out_directory.join(&path);
90 handles.push(spawn(compile(path, out_path, compiler.clone())))
91 }
92
93 for handle in handles {
94 handle.await??;
95 }
96
97 Ok(())
98}
99
100async fn compile(
101 src_path: PathBuf,
102 out_path: PathBuf,
103 compiler: Option<Arc<PathBuf>>,
104) -> Result<(), BuildError> {
105 let mut buffer = vec![];
106
107 if let Some(path) = compiler {
108 let mut command = Command::new(&*path)
109 .stdin(Stdio::piped())
110 .stdout(Stdio::piped())
111 .spawn()?;
112 let stdin = command.stdin.as_mut().expect("stdin");
113
114 stdin
115 .write_all(include_str!("prelude.scm").as_bytes())
116 .await?;
117 stdin.write_all(&read(src_path).await?).await?;
118
119 command.wait().await?;
120
121 command
122 .stdout
123 .expect("stdout")
124 .read_to_end(&mut buffer)
125 .await?;
126 } else {
127 compile_r7rs(&*read(&src_path).await?, &mut buffer)?;
128 }
129
130 if let Some(path) = out_path.parent() {
131 create_dir_all(path).await?;
132 }
133
134 write(out_path, &buffer).await?;
135
136 Ok(())
137}