folk-builder 0.2.1

Custom binary builder for Folk — generates and compiles a Folk server with selected plugins
Documentation
use std::path::Path;

use anyhow::{Context, Result};
use tempfile::TempDir;
use tracing::info;

use crate::codegen::{generate_build_rs, generate_cargo_toml, generate_lib_rs};
use crate::config::BuildConfig;

pub fn build(cfg: &BuildConfig, output_dir: &str) -> Result<()> {
    // Resolve paths to absolute (temp dir has different cwd).
    let mut resolved = cfg.clone();
    if let Some(path) = &resolved.build.folk_ext_path {
        let abs = std::fs::canonicalize(path)
            .with_context(|| format!("resolve folk_ext_path: {path}"))?;
        resolved.build.folk_ext_path = Some(abs.display().to_string());
    }
    for plugin in &mut resolved.plugin {
        if let Some(path) = &plugin.path {
            let abs = std::fs::canonicalize(path)
                .with_context(|| format!("resolve plugin path: {path}"))?;
            plugin.path = Some(abs.display().to_string());
        }
    }

    let tmp = TempDir::new().context("create temp dir")?;
    let tmp_path = tmp.path();

    std::fs::create_dir_all(tmp_path.join("src"))?;
    std::fs::write(tmp_path.join("Cargo.toml"), generate_cargo_toml(&resolved))?;
    std::fs::write(tmp_path.join("src/lib.rs"), generate_lib_rs(&resolved))?;
    std::fs::write(tmp_path.join("build.rs"), generate_build_rs())?;

    info!(dir = %tmp_path.display(), "compiling PHP extension");

    let status = std::process::Command::new("cargo")
        .args(["build", "--release"])
        .current_dir(tmp_path)
        .status()
        .context("run cargo")?;

    if !status.success() {
        anyhow::bail!("cargo build failed with status {status}");
    }

    let lib_name = &cfg.build.output;
    let ext = if cfg!(target_os = "macos") {
        "dylib"
    } else {
        "so"
    };

    let src = tmp_path
        .join("target/release")
        .join(format!("lib{lib_name}.{ext}"));
    let dst = Path::new(output_dir).join(format!("{lib_name}.{ext}"));

    std::fs::copy(&src, &dst)
        .with_context(|| format!("copy {}{}", src.display(), dst.display()))?;

    info!(output = %dst.display(), "extension ready");
    Ok(())
}