nectar/build.rs

221 lines
7.3 KiB
Rust
Raw Normal View History

2023-10-03 06:58:20 +03:00
use std::process::Command;
2023-10-06 07:12:31 +03:00
use std::{
fs, io,
io::{Read, Write},
};
2023-10-03 06:58:20 +03:00
fn run_command(cmd: &mut Command) -> io::Result<()> {
let status = cmd.status()?;
if status.success() {
Ok(())
} else {
Err(io::Error::new(io::ErrorKind::Other, "Command failed"))
}
}
2023-10-09 03:55:19 +03:00
fn file_outdated<P1, P2>(input: P1, output: P2) -> io::Result<bool>
where
P1: AsRef<std::path::Path>,
P2: AsRef<std::path::Path>,
{
let out_meta = std::fs::metadata(output);
if let Ok(meta) = out_meta {
let output_mtime = meta.modified()?;
// if input file is more recent than our output, we are outdated
let input_meta = fs::metadata(input)?;
let input_mtime = input_meta.modified()?;
Ok(input_mtime > output_mtime)
} else {
// output file not found, we are outdated
Ok(true)
}
}
2023-10-10 02:31:49 +03:00
fn build_app(target_path: &str, name: &str, parent_pkg_path: Option<&str>) {
let pwd = std::env::current_dir().unwrap();
let start = std::time::Instant::now();
// if and only if module's wit is outdated, re-set-up build environment
2023-10-10 02:31:49 +03:00
if file_outdated(
format!("{}/wit/uqbar.wit", pwd.display()),
format!("{}/wit/uqbar.wit", target_path),
2023-10-10 02:31:49 +03:00
)
.unwrap_or(true)
{
println!("cargo:warning=wit outdated, rebuilding");
2023-11-02 00:09:47 +03:00
run_command(Command::new("cp").args(&[
"-r",
&format!("{}/wit", pwd.display()),
&format!("{}/wit", target_path),
]))
.unwrap();
2023-10-10 02:31:49 +03:00
// create target/bindings directory
2023-10-10 02:32:23 +03:00
fs::create_dir_all(&format!("{}/target/bindings/{}", target_path, name,)).unwrap();
2023-10-10 02:31:49 +03:00
// copy newly-made target.wasm into target/bindings
run_command(Command::new("cp").args(&[
"target.wasm",
2023-10-10 02:32:23 +03:00
&format!("{}/target/bindings/{}/", target_path, name,),
2023-10-10 02:31:49 +03:00
]))
.unwrap();
// copy newly-made world into target/bindings
run_command(Command::new("cp").args(&[
"world",
2023-10-10 02:32:23 +03:00
&format!("{}/target/bindings/{}/", target_path, name,),
2023-10-10 02:31:49 +03:00
]))
.unwrap();
}
// Build the module targeting wasm32-wasi
let bash_build_path = &format!("{}/build.sh", target_path);
if std::path::Path::new(&bash_build_path).exists() {
let cwd = std::env::current_dir().unwrap();
std::env::set_current_dir(target_path).unwrap();
run_command(&mut Command::new("/bin/bash").arg("build.sh")).unwrap();
std::env::set_current_dir(cwd).unwrap();
} else {
run_command(Command::new("cargo").args(&[
"+nightly",
"build",
"--release",
"--no-default-features",
&format!("--manifest-path={}/Cargo.toml", target_path),
"--target",
"wasm32-wasi",
]))
.unwrap();
}
2023-10-10 02:31:49 +03:00
// Adapt module to component with adapter based on wasi_snapshot_preview1.wasm
run_command(Command::new("wasm-tools").args(&[
"component",
"new",
&format!("{}/target/wasm32-wasi/release/{}.wasm", target_path, name),
"-o",
2023-10-10 02:32:23 +03:00
&format!(
"{}/target/wasm32-wasi/release/{}_adapted.wasm",
target_path, name
),
2023-10-10 02:31:49 +03:00
"--adapt",
&format!("{}/wasi_snapshot_preview1.wasm", pwd.display()),
]))
.unwrap();
// Determine the destination for the .wasm file after embedding wit
let wasm_dest_path = if let Some(parent_pkg) = parent_pkg_path {
format!("{}/{}.wasm", parent_pkg, name)
} else {
let pkg_folder = format!("{}/pkg/", target_path);
let _ = run_command(Command::new("mkdir").args(&["-p", &pkg_folder]));
format!("{}/{}.wasm", pkg_folder, name)
};
// Embed "wit" into the component
run_command(Command::new("wasm-tools").args(&[
"component",
"embed",
"wit",
"--world",
"uq-process",
2023-10-10 02:32:23 +03:00
&format!(
"{}/target/wasm32-wasi/release/{}_adapted.wasm",
target_path, name
),
2023-10-10 02:31:49 +03:00
"-o",
&wasm_dest_path,
]))
.unwrap();
let end = std::time::Instant::now();
println!(
"cargo:warning=building {} took {:?}",
target_path,
end.duration_since(start)
);
2023-10-10 02:31:49 +03:00
}
2023-10-03 06:58:20 +03:00
fn main() {
if std::env::var("SKIP_BUILD_SCRIPT").is_ok() {
println!("Skipping build script");
return;
}
let pwd = std::env::current_dir().unwrap();
2023-10-10 02:31:49 +03:00
// Create target.wasm (compiled .wit) & world
2023-10-03 06:58:20 +03:00
run_command(Command::new("wasm-tools").args(&[
"component",
"wit",
&format!("{}/wit/", pwd.display()),
"-o",
"target.wasm",
"--wasm",
]))
.unwrap();
run_command(Command::new("touch").args(&[&format!("{}/world", pwd.display())])).unwrap();
// Build wasm32-wasi apps.
2023-10-10 02:31:49 +03:00
let modules_dir = format!("{}/modules", pwd.display());
for entry in std::fs::read_dir(&modules_dir).unwrap() {
let entry_path = entry.unwrap().path();
2023-10-12 23:06:51 +03:00
let package_name = entry_path.file_name().unwrap().to_str().unwrap();
2023-11-02 22:35:35 +03:00
if package_name != "terminal" {
continue;
}
2023-10-10 02:31:49 +03:00
// If Cargo.toml is present, build the app
2023-10-10 08:09:20 +03:00
let parent_pkg_path = format!("{}/pkg", entry_path.display());
2023-10-10 02:31:49 +03:00
if entry_path.join("Cargo.toml").exists() {
2023-10-12 23:07:21 +03:00
build_app(&entry_path.display().to_string(), &package_name, None);
2023-10-10 02:31:49 +03:00
} else if entry_path.is_dir() {
fs::create_dir_all(&parent_pkg_path).unwrap();
// Otherwise, consider it a directory containing subdirectories with potential apps
for sub_entry in std::fs::read_dir(&entry_path).unwrap() {
let sub_entry_path = sub_entry.unwrap().path();
if sub_entry_path.join("Cargo.toml").exists() {
2023-10-10 02:32:23 +03:00
build_app(
&sub_entry_path.display().to_string(),
&sub_entry_path.file_name().unwrap().to_str().unwrap(),
Some(&parent_pkg_path),
);
2023-10-10 02:31:49 +03:00
}
}
2023-10-10 08:09:20 +03:00
}
2023-10-10 02:31:49 +03:00
2023-10-10 08:09:20 +03:00
// After processing all sub-apps, zip the parent's pkg/ directory
let writer = std::fs::File::create(format!(
"{}/target/{}.zip",
pwd.display(),
entry_path.file_name().unwrap().to_str().unwrap()
))
.unwrap();
let options = zip::write::FileOptions::default()
.compression_method(zip::CompressionMethod::Stored)
.unix_permissions(0o755);
let mut zip = zip::ZipWriter::new(writer);
for sub_entry in walkdir::WalkDir::new(&parent_pkg_path) {
let sub_entry = sub_entry.unwrap();
let path = sub_entry.path();
let name = path
.strip_prefix(std::path::Path::new(&parent_pkg_path))
.unwrap();
// Write a directory or file to the ZIP archive
if path.is_file() {
zip.start_file(name.to_string_lossy().into_owned(), options)
.unwrap();
let mut file = std::fs::File::open(path).unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
zip.write_all(&buffer).unwrap();
} else if name.as_os_str().len() != 0 {
zip.add_directory(name.to_string_lossy().into_owned(), options)
2023-10-10 02:32:23 +03:00
.unwrap();
2023-10-06 07:12:31 +03:00
}
}
2023-10-10 08:09:20 +03:00
zip.finish().unwrap();
2023-10-03 06:58:20 +03:00
}
2023-10-03 06:58:48 +03:00
}