Skip to main content

folk_builder/
runner.rs

1use std::path::Path;
2
3use anyhow::{Context, Result};
4use tempfile::TempDir;
5use tracing::info;
6
7use crate::codegen::{generate_build_rs, generate_cargo_toml, generate_lib_rs};
8use crate::config::BuildConfig;
9
10pub fn build(cfg: &BuildConfig, output_dir: &str) -> Result<()> {
11    // Resolve paths to absolute (temp dir has different cwd).
12    let mut resolved = cfg.clone();
13    if let Some(path) = &resolved.build.folk_ext_path {
14        let abs = std::fs::canonicalize(path)
15            .with_context(|| format!("resolve folk_ext_path: {path}"))?;
16        resolved.build.folk_ext_path = Some(abs.display().to_string());
17    }
18    if let Some(path) = &resolved.build.folk_api_path {
19        let abs = std::fs::canonicalize(path)
20            .with_context(|| format!("resolve folk_api_path: {path}"))?;
21        resolved.build.folk_api_path = Some(abs.display().to_string());
22    }
23    for plugin in &mut resolved.plugin {
24        if let Some(path) = &plugin.path {
25            let abs = std::fs::canonicalize(path)
26                .with_context(|| format!("resolve plugin path: {path}"))?;
27            plugin.path = Some(abs.display().to_string());
28        }
29    }
30
31    let tmp = TempDir::new().context("create temp dir")?;
32    let tmp_path = tmp.path();
33
34    std::fs::create_dir_all(tmp_path.join("src"))?;
35    std::fs::write(tmp_path.join("Cargo.toml"), generate_cargo_toml(&resolved))?;
36    std::fs::write(tmp_path.join("src/lib.rs"), generate_lib_rs(&resolved))?;
37    std::fs::write(tmp_path.join("build.rs"), generate_build_rs())?;
38
39    info!(dir = %tmp_path.display(), "compiling PHP extension");
40
41    let status = std::process::Command::new("cargo")
42        .args(["build", "--release"])
43        .current_dir(tmp_path)
44        .status()
45        .context("run cargo")?;
46
47    if !status.success() {
48        anyhow::bail!("cargo build failed with status {status}");
49    }
50
51    let lib_name = &cfg.build.output;
52    let ext = if cfg!(target_os = "macos") {
53        "dylib"
54    } else {
55        "so"
56    };
57
58    let src = tmp_path
59        .join("target/release")
60        .join(format!("lib{lib_name}.{ext}"));
61    let dst = Path::new(output_dir).join(format!("{lib_name}.{ext}"));
62
63    std::fs::copy(&src, &dst)
64        .with_context(|| format!("copy {} → {}", src.display(), dst.display()))?;
65
66    info!(output = %dst.display(), "extension ready");
67    Ok(())
68}