#[derive(Debug, Clone)]
pub struct NapiAutoBuildConfig {
pub package_name: String,
pub prefer_npx: bool,
pub extra_args: Vec<String>,
}
impl NapiAutoBuildConfig {
pub fn new(package_name: impl Into<String>) -> Self {
Self {
package_name: package_name.into(),
prefer_npx: true,
extra_args: vec![],
}
}
pub fn with_args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
self.extra_args = args.into_iter().map(|s| s.into()).collect();
self
}
}
#[cfg(unix)]
pub fn napi_auto_build(package_name: impl Into<String>) {
napi_auto_build_with_config(NapiAutoBuildConfig::new(package_name));
}
#[cfg(not(unix))]
pub fn napi_auto_build(_package_name: impl Into<String>) {
println!("cargo:warning=napi_auto_build is only supported on Unix systems");
}
#[cfg(unix)]
pub fn napi_auto_build_with_config(config: NapiAutoBuildConfig) {
use std::process::Command;
if std::env::var("NAPI_BUILD_SKIP_WATCHER").is_ok() {
return;
}
let parent_pid = unsafe { libc::getppid() };
let ancestry_check = Command::new("sh")
.args([
"-c",
&format!(
"ps -o command= -p $(ps -o ppid= -p {}) 2>/dev/null | grep -qE '(napi|node.*napi)'",
parent_pid
),
])
.status();
if ancestry_check.map(|s| s.success()).unwrap_or(false) {
return;
}
let crate_dir = std::env::current_dir().unwrap_or_default();
let profile = std::env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
let release_flag = if profile == "release" {
"--release"
} else {
""
};
let extra_args = config.extra_args.join(" ");
let package_name = config.package_name;
let script = format!(
r#"
# Wait for cargo (PID {parent_pid}) to finish
while kill -0 {parent_pid} 2>/dev/null; do
sleep 0.3
done
# Small delay to ensure all file handles are released
sleep 0.2
# Run napi build from the crate directory
cd "{crate_dir}"
# Set env var to prevent recursion
export NAPI_BUILD_SKIP_WATCHER=1
# Try npx first, then bunx
if command -v npx >/dev/null 2>&1; then
npx -y -p @napi-rs/cli napi build --platform {release_flag} -p {package_name} {extra_args} 2>/dev/null
elif command -v bunx >/dev/null 2>&1; then
bunx -y @napi-rs/cli napi build --platform {release_flag} -p {package_name} {extra_args} 2>/dev/null
fi
"#,
parent_pid = parent_pid,
crate_dir = crate_dir.display(),
release_flag = release_flag,
package_name = package_name,
extra_args = extra_args,
);
let _ = Command::new("sh")
.args(["-c", &format!("({}) &", script.trim())])
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn();
}
#[cfg(not(unix))]
pub fn napi_auto_build_with_config(_config: NapiAutoBuildConfig) {
println!("cargo:warning=napi_auto_build is only supported on Unix systems");
}